[cogl/wip/cogl-1.14: 162/177] Allow lazy texture storage allocation



commit d2309a82e915e0e7b5fa93072bcf405f1ecbfa5d
Author: Robert Bragg <robert linux intel com>
Date:   Thu Nov 22 23:01:08 2012 +0000

    Allow lazy texture storage allocation
    
    Consistent with how we lazily allocate framebuffers this patch allows us
    to instantiate textures but still specify constraints and requirements
    before allocating storage so that we can be sure to allocate the most
    appropriate/efficient storage.
    
    This adds a cogl_texture_allocate() function that is analogous to
    cogl_framebuffer_allocate() which can optionally be called to explicitly
    allocate storage and catch any errors. If this function isn't used
    explicitly then Cogl will implicitly ensure textures are allocated
    before the storage is needed.
    
    It is generally recommended to rely on lazy storage allocation or at
    least perform explicit allocation as late as possible so Cogl can be
    fully informed about the best way to allocate storage.
    
    Reviewed-by: Neil Roberts <neil linux intel com>
    
    (cherry picked from commit 1fa7c0f10a8a03043e3c75cb079a49625df098b7)
    
    Note: This reverts the cogl_texture_rectangle_new_with_size API change
    that dropped the CoglError argument and keeps the semantics of
    allocating the texture immediately. This is because Mutter currently
    uses this API so we will probably look at updating this later once
    we have a corresponding Mutter patch prepared. The other API changes
    were kept since they only affected experimental api.

 cogl-pango/cogl-pango-glyph-cache.c           |    9 +-
 cogl/cogl-atlas-texture-private.h             |    3 +-
 cogl/cogl-atlas-texture.c                     |   90 ++++++--
 cogl/cogl-atlas.c                             |   22 ++-
 cogl/cogl-auto-texture.c                      |   24 ++-
 cogl/cogl-blit.c                              |   34 ++--
 cogl/cogl-driver.h                            |   13 +-
 cogl/cogl-framebuffer-private.h               |    2 +-
 cogl/cogl-framebuffer.c                       |   67 +++---
 cogl/cogl-sub-texture.c                       |   10 +
 cogl/cogl-texture-2d-private.h                |    2 +-
 cogl/cogl-texture-2d-sliced-private.h         |    1 +
 cogl/cogl-texture-2d-sliced.c                 |  303 +++++++++++++++----------
 cogl/cogl-texture-2d-sliced.h                 |   44 +++--
 cogl/cogl-texture-2d.c                        |   38 ++--
 cogl/cogl-texture-2d.h                        |   15 +-
 cogl/cogl-texture-3d-private.h                |    2 +-
 cogl/cogl-texture-3d.c                        |   76 ++++---
 cogl/cogl-texture-3d.h                        |   26 ++-
 cogl/cogl-texture-private.h                   |    8 +
 cogl/cogl-texture-rectangle-private.h         |    2 +-
 cogl/cogl-texture-rectangle.c                 |   87 +++++---
 cogl/cogl-texture.c                           |   50 ++++-
 cogl/cogl-texture.h                           |   31 +++-
 cogl/driver/gl/cogl-framebuffer-gl.c          |   15 +-
 cogl/driver/gl/cogl-texture-2d-gl-private.h   |    9 +-
 cogl/driver/gl/cogl-texture-2d-gl.c           |   70 ++++--
 cogl/driver/gl/gl/cogl-driver-gl.c            |    2 +-
 cogl/driver/gl/gles/cogl-driver-gles.c        |    2 +-
 cogl/driver/nop/cogl-driver-nop.c             |    2 +-
 cogl/driver/nop/cogl-texture-2d-nop-private.h |    9 +-
 cogl/driver/nop/cogl-texture-2d-nop.c         |   22 +--
 cogl/winsys/cogl-texture-pixmap-x11.c         |   10 +
 cogl/winsys/cogl-winsys-glx.c                 |   10 +-
 tests/conform/test-gles2-context.c            |    6 +-
 tests/conform/test-offscreen.c                |    6 +-
 tests/conform/test-utils.c                    |    6 +-
 37 files changed, 711 insertions(+), 417 deletions(-)
---
diff --git a/cogl-pango/cogl-pango-glyph-cache.c b/cogl-pango/cogl-pango-glyph-cache.c
index 14c8295..59cb5f4 100644
--- a/cogl-pango/cogl-pango-glyph-cache.c
+++ b/cogl-pango/cogl-pango-glyph-cache.c
@@ -214,6 +214,7 @@ cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache,
                                             CoglPangoGlyphCacheValue *value)
 {
   CoglAtlasTexture *texture;
+  CoglError *ignore_error = NULL;
 
   if (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SHARED_ATLAS))
     return FALSE;
@@ -227,10 +228,14 @@ cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache,
                                                value->draw_width,
                                                value->draw_height,
                                                COGL_TEXTURE_NONE,
-                                               COGL_PIXEL_FORMAT_RGBA_8888_PRE);
+                                               COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                               &ignore_error);
 
   if (texture == NULL)
-    return FALSE;
+    {
+      cogl_error_free (ignore_error);
+      return FALSE;
+    }
 
   value->texture = COGL_TEXTURE (texture);
   value->tx1 = 0;
diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h
index 52fdf06..670eea4 100644
--- a/cogl/cogl-atlas-texture-private.h
+++ b/cogl/cogl-atlas-texture-private.h
@@ -68,7 +68,8 @@ _cogl_atlas_texture_new_with_size (CoglContext *ctx,
                                    int width,
                                    int height,
                                    CoglTextureFlags flags,
-                                   CoglPixelFormat internal_format);
+                                   CoglPixelFormat internal_format,
+                                   CoglError **error);
 
 void
 _cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx,
diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c
index c37908a..7f62efc 100644
--- a/cogl/cogl-atlas-texture.c
+++ b/cogl/cogl-atlas-texture.c
@@ -651,32 +651,60 @@ _cogl_atlas_texture_new_with_size (CoglContext *ctx,
                                    int width,
                                    int height,
                                    CoglTextureFlags flags,
-                                   CoglPixelFormat internal_format)
+                                   CoglPixelFormat internal_format,
+                                   CoglError **error)
 {
   CoglAtlasTexture *atlas_tex;
-  CoglAtlas *atlas;
-  GSList *l;
 
   /* Don't put textures in the atlas if the user has explicitly
      requested to disable it */
   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ATLAS)))
-    return NULL;
+    {
+      _cogl_set_error (error,
+                       COGL_SYSTEM_ERROR,
+                       COGL_SYSTEM_ERROR_UNSUPPORTED,
+                       "Atlasing disabled");
+      return NULL;
+    }
 
   /* We can't put the texture in the atlas if there are any special
      flags. This precludes textures with COGL_TEXTURE_NO_ATLAS and
      COGL_TEXTURE_NO_SLICING from being atlased */
   if (flags)
-    return NULL;
+    {
+      /* XXX: This is a bit of an odd error; if we make this api
+       * public then this should probably be dealt with at a higher
+       * level, in cogl-auto-texture.c:cogl_texture_new_with_size().
+       */
+      _cogl_set_error (error,
+                       COGL_SYSTEM_ERROR,
+                       COGL_SYSTEM_ERROR_UNSUPPORTED,
+                       "Usage constraints preclude atlasing texture");
+      return NULL;
+    }
 
   /* We can't atlas zero-sized textures because it breaks the atlas
      data structure */
   if (width < 1 || height < 1)
-    return NULL;
+    {
+      _cogl_set_error (error,
+                       COGL_TEXTURE_ERROR,
+                       COGL_TEXTURE_ERROR_SIZE,
+                       "1x1 atlas textures not supported");
+      return NULL;
+    }
 
   /* If we can't use FBOs then it will be too slow to migrate textures
      and we shouldn't use the atlas */
   if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
-    return NULL;
+    {
+      _cogl_set_error (error,
+                       COGL_SYSTEM_ERROR,
+                       COGL_SYSTEM_ERROR_UNSUPPORTED,
+                       "Atlasing disabled because migrations "
+                       "would be too slow");
+      return NULL;
+    }
 
   COGL_NOTE (ATLAS, "Adding texture of size %ix%i", width, height);
 
@@ -685,7 +713,10 @@ _cogl_atlas_texture_new_with_size (CoglContext *ctx,
     {
       COGL_NOTE (ATLAS, "Texture can not be added because the "
                  "format is unsupported");
-
+      _cogl_set_error (error,
+                       COGL_TEXTURE_ERROR,
+                       COGL_TEXTURE_ERROR_FORMAT,
+                       "Texture format unsuitable for atlasing");
       return NULL;
     }
 
@@ -703,12 +734,27 @@ _cogl_atlas_texture_new_with_size (CoglContext *ctx,
 
   atlas_tex->sub_texture = NULL;
 
+  atlas_tex->format = internal_format;
+  atlas_tex->atlas = NULL;
+
+  return _cogl_atlas_texture_object_new (atlas_tex);
+}
+
+static CoglBool
+_cogl_atlas_texture_allocate (CoglTexture *tex,
+                              CoglError **error)
+{
+  CoglContext *ctx = tex->context;
+  CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex);
+  CoglAtlas *atlas;
+  GSList *l;
+
   /* Look for an existing atlas that can hold the texture */
   for (l = ctx->atlases; l; l = l->next)
     /* 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 */
-                                   width + 2, height + 2,
+                                   tex->width + 2, tex->height + 2,
                                    atlas_tex))
       {
         cogl_object_ref (atlas);
@@ -722,20 +768,23 @@ _cogl_atlas_texture_new_with_size (CoglContext *ctx,
       COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas);
       if (!_cogl_atlas_reserve_space (atlas,
                                       /* Add two pixels for the border */
-                                      width + 2, height + 2,
+                                      tex->width + 2, tex->height + 2,
                                       atlas_tex))
         {
           /* Ok, this means we really can't add it to the atlas */
           cogl_object_unref (atlas);
-          g_free (atlas_tex);
-          return NULL;
+
+          _cogl_set_error (error,
+                           COGL_SYSTEM_ERROR,
+                           COGL_SYSTEM_ERROR_NO_MEMORY,
+                           "Not enough memory to atlas texture");
+          return FALSE;
         }
     }
 
-  atlas_tex->format = internal_format;
   atlas_tex->atlas = atlas;
 
-  return _cogl_atlas_texture_object_new (atlas_tex);
+  return TRUE;
 }
 
 CoglAtlasTexture *
@@ -762,14 +811,14 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
 
   atlas_tex = _cogl_atlas_texture_new_with_size (ctx,
                                                  bmp_width, bmp_height,
-                                                 flags, internal_format);
+                                                 flags, internal_format,
+                                                 error);
+  if (!atlas_tex)
+    return NULL;
 
-  if (atlas_tex == NULL)
+  if (!cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error))
     {
-      _cogl_set_error (error,
-                       COGL_SYSTEM_ERROR,
-                       COGL_SYSTEM_ERROR_UNSUPPORTED,
-                       "Texture type not compatible with atlas");
+      cogl_object_unref (atlas_tex);
       return NULL;
     }
 
@@ -839,6 +888,7 @@ static const CoglTextureVtable
 cogl_atlas_texture_vtable =
   {
     FALSE, /* not primitive */
+    _cogl_atlas_texture_allocate,
     _cogl_atlas_texture_set_region,
     NULL, /* get_data */
     _cogl_atlas_texture_foreach_sub_texture_in_region,
diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c
index 62b11f8..d6c0e74 100644
--- a/cogl/cogl-atlas.c
+++ b/cogl/cogl-atlas.c
@@ -301,6 +301,9 @@ _cogl_atlas_create_texture (CoglAtlas *atlas,
       tex = cogl_texture_2d_new_from_bitmap (clear_bmp,
                                              atlas->texture_format,
                                              &ignore_error);
+      if (!tex)
+        cogl_error_free (ignore_error);
+
       cogl_object_unref (clear_bmp);
 
       g_free (clear_data);
@@ -309,13 +312,15 @@ _cogl_atlas_create_texture (CoglAtlas *atlas,
     {
       tex = cogl_texture_2d_new_with_size (ctx,
                                            width, height,
-                                           atlas->texture_format,
-                                           &ignore_error);
+                                           atlas->texture_format);
+      if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error))
+        {
+          cogl_error_free (ignore_error);
+          cogl_object_unref (tex);
+          tex = NULL;
+        }
     }
 
-  if (!tex)
-    cogl_error_free (ignore_error);
-
   return tex;
 }
 
@@ -547,11 +552,18 @@ _cogl_atlas_copy_rectangle (CoglAtlas *atlas,
 {
   CoglTexture *tex;
   CoglBlitData blit_data;
+  CoglError *ignore_error = NULL;
 
   _COGL_GET_CONTEXT (ctx, NULL);
 
   /* Create a new texture at the right size */
   tex = cogl_texture_new_with_size (width, height, flags, format);
+  if (!cogl_texture_allocate (tex, &ignore_error))
+    {
+      cogl_error_free (ignore_error);
+      cogl_object_unref (tex);
+      return NULL;
+    }
 
   /* Blit the data out of the atlas to the new texture. If FBOs
      aren't available this will end up having to copy the entire
diff --git a/cogl/cogl-auto-texture.c b/cogl/cogl-auto-texture.c
index bcbb1d0..ff7673b 100644
--- a/cogl/cogl-auto-texture.c
+++ b/cogl/cogl-auto-texture.c
@@ -47,12 +47,13 @@
 #include "cogl-texture-2d-gl.h"
 
 CoglTexture *
-cogl_texture_new_with_size (unsigned int     width,
-			    unsigned int     height,
+cogl_texture_new_with_size (unsigned int width,
+			    unsigned int height,
                             CoglTextureFlags flags,
-			    CoglPixelFormat  internal_format)
+			    CoglPixelFormat internal_format)
 {
   CoglTexture *tex;
+  CoglError *skip_error = NULL;
 
   _COGL_GET_CONTEXT (ctx, NULL);
 
@@ -63,8 +64,18 @@ cogl_texture_new_with_size (unsigned int     width,
       /* First try creating a fast-path non-sliced texture */
       tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx,
                                                          width, height,
-                                                         internal_format,
-                                                         NULL));
+                                                         internal_format));
+
+      /* TODO: instead of allocating storage here it would be better
+       * if we had some api that let us just check that the size is
+       * supported by the hardware so storage could be allocated
+       * lazily when uploading data. */
+      if (!cogl_texture_allocate (tex, &skip_error))
+        {
+          cogl_error_free (skip_error);
+          cogl_object_unref (tex);
+          tex = NULL;
+        }
     }
   else
     tex = NULL;
@@ -83,8 +94,7 @@ cogl_texture_new_with_size (unsigned int     width,
                                                                 width,
                                                                 height,
                                                                 max_waste,
-                                                                internal_format,
-                                                                NULL));
+                                                                internal_format));
     }
 
   return tex;
diff --git a/cogl/cogl-blit.c b/cogl/cogl-blit.c
index 5cb5ea4..ea457b9 100644
--- a/cogl/cogl-blit.c
+++ b/cogl/cogl-blit.c
@@ -49,16 +49,15 @@ _cogl_blit_texture_render_begin (CoglBlitData *data)
   CoglFramebuffer *fb;
   CoglPipeline *pipeline;
   unsigned int dst_width, dst_height;
+  CoglError *ignore_error = NULL;
 
   offscreen = _cogl_offscreen_new_to_texture_full
     (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
 
-  if (offscreen == NULL)
-    return FALSE;
-
   fb = COGL_FRAMEBUFFER (offscreen);
-  if (!cogl_framebuffer_allocate (fb, NULL))
+  if (!cogl_framebuffer_allocate (fb, &ignore_error))
     {
+      cogl_error_free (ignore_error);
       cogl_object_unref (fb);
       return FALSE;
     }
@@ -148,6 +147,7 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data)
   CoglContext *ctx = data->src_tex->context;
   CoglOffscreen *dst_offscreen = NULL, *src_offscreen = NULL;
   CoglFramebuffer *dst_fb, *src_fb;
+  CoglError *ignore_error = NULL;
 
   /* We can only blit between FBOs if both textures are the same
      format and the blit framebuffer extension is supported */
@@ -158,24 +158,25 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data)
 
   dst_offscreen = _cogl_offscreen_new_to_texture_full
     (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
-  if (dst_offscreen == NULL)
-    return FALSE;
 
   dst_fb = COGL_FRAMEBUFFER (dst_offscreen);
-  if (!cogl_framebuffer_allocate (dst_fb, NULL))
-    goto error;
+  if (!cogl_framebuffer_allocate (dst_fb, &ignore_error))
+    {
+      cogl_error_free (ignore_error);
+      goto error;
+    }
 
   src_offscreen= _cogl_offscreen_new_to_texture_full
     (data->src_tex,
      COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL,
      0 /* level */);
 
-  if (src_offscreen == NULL)
-    goto error;
-
   src_fb = COGL_FRAMEBUFFER (src_offscreen);
-  if (!cogl_framebuffer_allocate (src_fb, NULL))
-    goto error;
+  if (!cogl_framebuffer_allocate (src_fb, &ignore_error))
+    {
+      cogl_error_free (ignore_error);
+      goto error;
+    }
 
   data->src_fb = src_fb;
   data->dest_fb = dst_fb;
@@ -220,6 +221,7 @@ _cogl_blit_copy_tex_sub_image_begin (CoglBlitData *data)
 {
   CoglOffscreen *offscreen;
   CoglFramebuffer *fb;
+  CoglError *ignore_error = NULL;
 
   /* This will only work if the target texture is a CoglTexture2D */
   if (!cogl_is_texture_2d (data->dst_tex))
@@ -228,12 +230,10 @@ _cogl_blit_copy_tex_sub_image_begin (CoglBlitData *data)
   offscreen = _cogl_offscreen_new_to_texture_full
     (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
 
-  if (offscreen == NULL)
-    return FALSE;
-
   fb = COGL_FRAMEBUFFER (offscreen);
-  if (!cogl_framebuffer_allocate (fb, NULL))
+  if (!cogl_framebuffer_allocate (fb, &ignore_error))
     {
+      cogl_error_free (ignore_error);
       cogl_object_unref (fb);
       return FALSE;
     }
diff --git a/cogl/cogl-driver.h b/cogl/cogl-driver.h
index e46bec8..0c42e71 100644
--- a/cogl/cogl-driver.h
+++ b/cogl/cogl-driver.h
@@ -138,14 +138,11 @@ struct _CoglDriverVtable
   void
   (* texture_2d_init) (CoglTexture2D *tex_2d);
 
-  /* Instantiates a new CoglTexture2D object with un-initialized
-   * storage for a given size and internal format */
-  CoglTexture2D *
-  (* texture_2d_new_with_size) (CoglContext *ctx,
-                                int width,
-                                int height,
-                                CoglPixelFormat internal_format,
-                                CoglError **error);
+  /* Allocates (uninitialized) storage for the given texture according
+   * to the configured size and format of the texture */
+  CoglBool
+  (* 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
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index 7664f7c..a88c591 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -306,7 +306,7 @@ _cogl_free_framebuffer_stack (GSList *stack);
 CoglOffscreen *
 _cogl_offscreen_new_to_texture_full (CoglTexture *texture,
                                      CoglOffscreenFlags create_flags,
-                                     unsigned int level);
+                                     int level);
 
 /*
  * _cogl_push_framebuffers:
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 148e7f4..8b95796 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -49,6 +49,7 @@
 #include "cogl-primitives-private.h"
 #include "cogl-path-private.h"
 #include "cogl-error-private.h"
+#include "cogl-texture-gl-private.h"
 
 typedef struct _CoglFramebufferStackEntry
 {
@@ -569,48 +570,24 @@ _cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer)
 CoglOffscreen *
 _cogl_offscreen_new_to_texture_full (CoglTexture *texture,
                                      CoglOffscreenFlags create_flags,
-                                     unsigned int level)
+                                     int level)
 {
   CoglContext *ctx = texture->context;
   CoglOffscreen *offscreen;
   CoglFramebuffer *fb;
   int level_width;
   int level_height;
-  int i;
   CoglOffscreen *ret;
 
-  if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
-    return NULL;
-
-  /* Make texture is a valid texture object */
-  if (!cogl_is_texture (texture))
-    return NULL;
-
-  /* The texture must not be sliced */
-  if (cogl_texture_is_sliced (texture))
-    return NULL;
-
-  /* Calculate the size of the texture at this mipmap level to ensure
-     that it's a valid level */
-  level_width = cogl_texture_get_width (texture);
-  level_height = cogl_texture_get_height (texture);
-
-  for (i = 0; i < level; i++)
-    {
-      /* If neither dimension can be further divided then the level is
-         invalid */
-      if (level_width == 1 && level_height == 1)
-        {
-          g_warning ("Invalid texture level passed to "
-                     "_cogl_offscreen_new_to_texture_full");
-          return NULL;
-        }
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_texture (texture), NULL);
+  _COGL_RETURN_VAL_IF_FAIL (level < _cogl_texture_get_n_levels (texture),
+                            NULL);
 
-      if (level_width > 1)
-        level_width >>= 1;
-      if (level_height > 1)
-        level_height >>= 1;
-    }
+  _cogl_texture_get_level_size (texture,
+                                level,
+                                &level_width,
+                                &level_height,
+                                NULL);
 
   offscreen = g_new0 (CoglOffscreen, 1);
   offscreen->texture = cogl_object_ref (texture);
@@ -688,7 +665,29 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
   else
     {
       CoglContext *ctx = framebuffer->context;
-      if (!ctx->driver_vtable->offscreen_allocate (COGL_OFFSCREEN (framebuffer), error))
+      CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer);
+
+      if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
+        {
+          _cogl_set_error (error, COGL_SYSTEM_ERROR,
+                           COGL_SYSTEM_ERROR_UNSUPPORTED,
+                           "Offscreen framebuffers not supported by system");
+          return FALSE;
+        }
+
+      if (cogl_texture_is_sliced (offscreen->texture))
+        {
+          _cogl_set_error (error, COGL_SYSTEM_ERROR,
+                           COGL_SYSTEM_ERROR_UNSUPPORTED,
+                           "Can't create offscreen framebuffer from "
+                           "sliced texture");
+          return FALSE;
+        }
+
+      if (!cogl_texture_allocate (offscreen->texture, error))
+        return FALSE;
+
+      if (!ctx->driver_vtable->offscreen_allocate (offscreen, error))
         return FALSE;
     }
 
diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c
index 18447f8..9a308be 100644
--- a/cogl/cogl-sub-texture.c
+++ b/cogl/cogl-sub-texture.c
@@ -259,6 +259,15 @@ cogl_sub_texture_new (CoglContext *ctx,
   return _cogl_sub_texture_object_new (sub_tex);
 }
 
+static CoglBool
+_cogl_sub_texture_allocate (CoglTexture *tex,
+                            CoglError **error)
+{
+  CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
+
+  return cogl_texture_allocate (sub_tex->full_texture, error);
+}
+
 CoglTexture *
 cogl_sub_texture_get_parent (CoglSubTexture *sub_texture)
 {
@@ -433,6 +442,7 @@ static const CoglTextureVtable
 cogl_sub_texture_vtable =
   {
     FALSE, /* not primitive */
+    _cogl_sub_texture_allocate,
     _cogl_sub_texture_set_region,
     NULL, /* get_data */
     _cogl_sub_texture_foreach_sub_texture_in_region,
diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h
index 3ae8ded..12e5820 100644
--- a/cogl/cogl-texture-2d-private.h
+++ b/cogl/cogl-texture-2d-private.h
@@ -39,7 +39,7 @@ struct _CoglTexture2D
 
   /* The internal format of the GL texture represented as a
      CoglPixelFormat */
-  CoglPixelFormat format;
+  CoglPixelFormat internal_format;
 
   CoglBool auto_mipmap;
   CoglBool mipmaps_dirty;
diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h
index 5bc4e5f..fbfd198 100644
--- a/cogl/cogl-texture-2d-sliced-private.h
+++ b/cogl/cogl-texture-2d-sliced-private.h
@@ -38,6 +38,7 @@ struct _CoglTexture2DSliced
   GArray *slice_y_spans;
   GArray *slice_textures;
   int max_waste;
+  CoglPixelFormat internal_format;
 };
 
 CoglTexture2DSliced *
diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c
index 51997bd..ef9d72d 100644
--- a/cogl/cogl-texture-2d-sliced.c
+++ b/cogl/cogl-texture-2d-sliced.c
@@ -656,20 +656,18 @@ _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
 }
 
 static CoglBool
-_cogl_texture_2d_sliced_slices_create (CoglContext *ctx,
-                                       CoglTexture2DSliced *tex_2ds,
-                                       int width, int height,
-                                       CoglPixelFormat format)
+_cogl_texture_2d_sliced_setup_spans (CoglContext *ctx,
+                                     CoglTexture2DSliced *tex_2ds,
+                                     int width,
+                                     int height,
+                                     int max_waste,
+                                     CoglPixelFormat format,
+                                     CoglError **error)
 {
   int max_width;
   int max_height;
-  CoglTexture2D **slice_textures;
   int n_x_slices;
   int n_y_slices;
-  int n_slices;
-  int x, y;
-  CoglSpan *x_span;
-  CoglSpan *y_span;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
@@ -697,7 +695,7 @@ _cogl_texture_2d_sliced_slices_create (CoglContext *ctx,
                                           &gl_type);
 
   /* Negative number means no slicing forced by the user */
-  if (tex_2ds->max_waste <= -1)
+  if (max_waste <= -1)
     {
       CoglSpan span;
 
@@ -710,6 +708,13 @@ _cogl_texture_2d_sliced_slices_create (CoglContext *ctx,
                                                 max_width,
                                                 max_height))
         {
+          _cogl_set_error (error,
+                           COGL_TEXTURE_ERROR,
+                           COGL_TEXTURE_ERROR_SIZE,
+                           "Sliced texture size of %d x %d not possible "
+                           "with max waste set to -1",
+                           width,
+                           height);
           return FALSE;
         }
 
@@ -753,16 +758,25 @@ _cogl_texture_2d_sliced_slices_create (CoglContext *ctx,
             max_height /= 2;
 
           if (max_width == 0 || max_height == 0)
-            return FALSE;
+            {
+              /* Maybe it would be ok to just g_warn_if_reached() for this
+               * codepath */
+              _cogl_set_error (error,
+                               COGL_TEXTURE_ERROR,
+                               COGL_TEXTURE_ERROR_SIZE,
+                               "No suitable slice geometry found");
+              return FALSE;
+
+            }
         }
 
       /* Determine the slices required to cover the bitmap area */
       n_x_slices = slices_for_size (width,
-                                    max_width, tex_2ds->max_waste,
+                                    max_width, max_waste,
                                     NULL);
 
       n_y_slices = slices_for_size (height,
-                                    max_height, tex_2ds->max_waste,
+                                    max_height, max_waste,
                                     NULL);
 
       /* Init span arrays with reserved size */
@@ -776,64 +790,20 @@ _cogl_texture_2d_sliced_slices_create (CoglContext *ctx,
 
       /* Fill span arrays with info */
       slices_for_size (width,
-                       max_width, tex_2ds->max_waste,
+                       max_width, max_waste,
                        tex_2ds->slice_x_spans);
 
       slices_for_size (height,
-                       max_height, tex_2ds->max_waste,
+                       max_height, max_waste,
                        tex_2ds->slice_y_spans);
     }
 
-  /* Init and resize GL handle array */
-  n_slices = n_x_slices * n_y_slices;
-
-  tex_2ds->slice_textures = g_array_sized_new (FALSE, FALSE,
-                                               sizeof (CoglTexture2D *),
-                                               n_slices);
-
-  g_array_set_size (tex_2ds->slice_textures, n_slices);
-
-  slice_textures = (CoglTexture2D **) tex_2ds->slice_textures->data;
-
-  /* Init each GL texture object */
-  for (y = 0; y < n_y_slices; ++y)
-    {
-      y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y);
-
-      for (x = 0; x < n_x_slices; ++x)
-        {
-          CoglError *error = NULL;
-          x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x);
-
-          COGL_NOTE (SLICING, "CREATE SLICE (%d,%d)\tsize (%d,%d)",
-                     x, y,
-                     (int)(x_span->size - x_span->waste),
-                     (int)(y_span->size - y_span->waste));
-
-          slice_textures[y * n_x_slices + x] =
-            cogl_texture_2d_new_with_size (ctx, x_span->size, y_span->size,
-                                           format, &error);
-          if (!slice_textures[y * n_x_slices + x])
-            {
-              g_array_set_size (tex_2ds->slice_textures, y * n_x_slices + x);
-              cogl_error_free (error);
-              return FALSE;
-            }
-        }
-    }
-
   return TRUE;
 }
 
 static void
 _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *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);
-
   if (tex_2ds->slice_textures != NULL)
     {
       int i;
@@ -854,6 +824,12 @@ _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));
 }
@@ -863,76 +839,148 @@ _cogl_texture_2d_sliced_init_base (CoglContext *ctx,
                                    CoglTexture2DSliced *tex_2ds,
                                    int width,
                                    int height,
-                                   CoglPixelFormat internal_format)
+                                   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->slice_x_spans = NULL;
-  tex_2ds->slice_y_spans = NULL;
-  tex_2ds->slice_textures = NULL;
-
-  /* Create slices for the given format and size */
-  if (!_cogl_texture_2d_sliced_slices_create (ctx,
-                                              tex_2ds,
-                                              width,
-                                              height,
-                                              internal_format))
-    return FALSE;
+  tex_2ds->max_waste = max_waste;
+  tex_2ds->internal_format = internal_format;
 
-  return TRUE;
+  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,
-                                      unsigned int width,
-                                      unsigned int height,
+                                      int width,
+                                      int height,
                                       int max_waste,
-                                      CoglPixelFormat internal_format,
-                                      CoglError **error)
+                                      CoglPixelFormat internal_format)
 {
-  CoglTexture2DSliced   *tex_2ds;
+  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_new (CoglTexture2DSliced, 1);
-
-  tex_2ds->max_waste = max_waste;
+  tex_2ds = g_new0 (CoglTexture2DSliced, 1);
 
   if (!_cogl_texture_2d_sliced_init_base (ctx,
                                           tex_2ds,
                                           width, height,
-                                          internal_format))
+                                          max_waste,
+                                          internal_format,
+                                          &ignore_error))
     {
-      _cogl_texture_2d_sliced_free (tex_2ds);
-      _cogl_set_error (error,
-                       COGL_SYSTEM_ERROR,
-                       COGL_SYSTEM_ERROR_NO_MEMORY,
-                       "Not enough memory to allocate texture slices");
-      return NULL;
+      /* 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)
+    {
+      _cogl_set_error (error,
+                       COGL_TEXTURE_ERROR,
+                       COGL_TEXTURE_ERROR_SIZE,
+                       "Couldn't find suitable slicing geometry "
+                       "for given size");
+      return FALSE;
+    }
+
+  n_x_slices = tex_2ds->slice_x_spans->len;
+  n_y_slices = tex_2ds->slice_y_spans->len;
+  n_slices = n_x_slices * n_y_slices;
+
+  tex_2ds->slice_textures = g_array_sized_new (FALSE, FALSE,
+                                               sizeof (CoglTexture2D *),
+                                               n_slices);
+
+  g_array_set_size (tex_2ds->slice_textures, n_slices);
+
+  /* Allocate each slice */
+  for (y = 0; y < n_y_slices; ++y)
+    {
+      y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y);
+
+      for (x = 0; x < n_x_slices; ++x)
+        {
+          CoglTexture *slice;
+
+          x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x);
+
+          COGL_NOTE (SLICING, "CREATE SLICE (%d,%d)\tsize (%d,%d)",
+                     x, y,
+                     (int)(x_span->size - x_span->waste),
+                     (int)(y_span->size - y_span->waste));
+
+          slice = COGL_TEXTURE (
+            cogl_texture_2d_new_with_size (ctx,
+                                           x_span->size, y_span->size,
+                                           format));
+          g_array_append_val (tex_2ds->slice_textures, slice);
+          if (!cogl_texture_allocate (slice, error))
+            {
+              _cogl_texture_2d_sliced_slices_free (tex_2ds);
+              return FALSE;
+            }
+        }
+    }
+
+  return TRUE;
+}
+
 CoglTexture2DSliced *
 _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp,
                                          CoglTextureFlags flags,
                                          CoglPixelFormat internal_format,
                                          CoglError **error)
 {
+  CoglContext *ctx;
   CoglTexture2DSliced *tex_2ds;
-  CoglBitmap          *dst_bmp;
-  GLenum               gl_intformat;
-  GLenum               gl_format;
-  GLenum               gl_type;
-  int                  width, height;
-  CoglContext         *ctx;
-  int                  i;
+  CoglBitmap *dst_bmp;
+  GLenum gl_intformat;
+  GLenum gl_format;
+  GLenum gl_type;
+  int width, height, max_waste;
+  int i;
 
   _COGL_RETURN_VAL_IF_FAIL (cogl_is_bitmap (bmp), NULL);
 
@@ -945,9 +993,9 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp,
   tex_2ds = g_new0 (CoglTexture2DSliced, 1);
 
   if (flags & COGL_TEXTURE_NO_SLICING)
-    tex_2ds->max_waste = -1;
+    max_waste = -1;
   else
-    tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE;
+    max_waste = COGL_TEXTURE_MAX_WASTE;
 
   dst_bmp = _cogl_texture_prepare_for_upload (bmp,
                                               internal_format,
@@ -962,10 +1010,17 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp,
       return NULL;
     }
 
+  /* 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,
-                                          internal_format))
+                                          max_waste,
+                                          internal_format,
+                                          error))
+    goto error;
+
+  if (!cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error))
     goto error;
 
   if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds,
@@ -1054,6 +1109,7 @@ _cogl_texture_2d_sliced_new_from_foreign (CoglContext *ctx,
                       &cogl_texture_2d_sliced_vtable);
 
   tex_2ds->max_waste = 0;
+  tex_2ds->internal_format = format;
 
   /* Create slice arrays */
   tex_2ds->slice_x_spans =
@@ -1081,6 +1137,8 @@ _cogl_texture_2d_sliced_new_from_foreign (CoglContext *ctx,
 
   g_array_append_val (tex_2ds->slice_textures, tex_2d);
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds), TRUE);
+
   return _cogl_texture_2d_sliced_object_new (tex_2ds);
 }
 
@@ -1112,13 +1170,23 @@ _cogl_texture_2d_sliced_is_sliced (CoglTexture *tex)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
 
-  if (tex_2ds->slice_textures == NULL)
+  /* 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;
 
-  if (tex_2ds->slice_textures->len <= 1)
+  if (tex_2ds->slice_x_spans->len != 1 ||
+      tex_2ds->slice_y_spans->len != 1)
+    return TRUE;
+  else
     return FALSE;
-
-  return TRUE;
 }
 
 static CoglBool
@@ -1223,12 +1291,10 @@ _cogl_texture_2d_sliced_gl_flush_legacy_texobj_filters (CoglTexture *tex,
                                                         GLenum mag_filter)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
-  CoglTexture2D       *slice_tex;
-  int                  i;
+  CoglTexture2D *slice_tex;
+  int i;
 
-  /* Make sure slices were created */
-  if (tex_2ds->slice_textures == NULL)
-    return;
+  _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL);
 
   /* Apply new filters to every slice. The slice texture itself should
      cache the value and avoid resubmitting the same filter value to
@@ -1246,11 +1312,9 @@ _cogl_texture_2d_sliced_pre_paint (CoglTexture *tex,
                                    CoglTexturePrePaintFlags flags)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
-  int                  i;
+  int i;
 
-  /* Make sure slices were created */
-  if (tex_2ds->slice_textures == NULL)
-    return;
+  _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL);
 
   /* Pass the pre-paint on to every slice */
   for (i = 0; i < tex_2ds->slice_textures->len; i++)
@@ -1267,9 +1331,7 @@ _cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex)
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
   int i;
 
-  /* Make sure slices were created */
-  if (tex_2ds->slice_textures == NULL)
-    return;
+  _COGL_RETURN_IF_FAIL (tex_2ds->slice_textures != NULL);
 
   /* Pass the call on to every slice */
   for (i = 0; i < tex_2ds->slice_textures->len; i++)
@@ -1327,15 +1389,8 @@ static CoglPixelFormat
 _cogl_texture_2d_sliced_get_format (CoglTexture *tex)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
-  CoglTexture2D *slice_tex;
-
-  /* Make sure slices were created */
-  if (tex_2ds->slice_textures == NULL)
-    return 0;
 
-  /* Pass the call on to the first slice */
-  slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0);
-  return cogl_texture_get_format (COGL_TEXTURE (slice_tex));
+  return tex_2ds->internal_format;
 }
 
 static GLenum
@@ -1344,9 +1399,8 @@ _cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex)
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
   CoglTexture2D *slice_tex;
 
-  /* Make sure slices were created */
-  if (tex_2ds->slice_textures == NULL)
-    return 0;
+  /* Assert that we've allocated our slices at this point */
+  cogl_texture_allocate (tex, NULL); /* (abort on error) */
 
   /* Pass the call on to the first slice */
   slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0);
@@ -1363,6 +1417,7 @@ static const CoglTextureVtable
 cogl_texture_2d_sliced_vtable =
   {
     FALSE, /* not primitive */
+    _cogl_texture_2d_sliced_allocate,
     _cogl_texture_2d_sliced_set_region,
     NULL, /* get_data */
     _cogl_texture_2d_sliced_foreach_sub_texture_in_region,
diff --git a/cogl/cogl-texture-2d-sliced.h b/cogl/cogl-texture-2d-sliced.h
index 8d2cc4b..e26157e 100644
--- a/cogl/cogl-texture-2d-sliced.h
+++ b/cogl/cogl-texture-2d-sliced.h
@@ -70,32 +70,48 @@ typedef struct _CoglTexture2DSliced CoglTexture2DSliced;
  * @width: The virtual width of your sliced texture.
  * @height: The virtual height of your sliced texture.
  * @max_waste: The threshold of how wide a strip of wasted texels
- *             are allowed in the non-power-of-two textures before
- *             they must be sliced to reduce the amount of waste.
+ *             are allowed along the right and bottom textures before
+ *             they must be sliced to reduce the amount of waste. A
+ *             negative can be passed to disable slicing.
  * @internal_format: The format of the texture
- * @error: A #CoglError for exceptions.
  *
  * Creates a #CoglTexture2DSliced that may internally be comprised of
- * 1 or more #CoglTexture2D textures with power-of-two sizes.
+ * 1 or more #CoglTexture2D textures depending on GPU limitations.
+ * For example if the GPU only supports power-of-two sized textures
+ * then a sliced texture will turn a non-power-of-two size into a
+ * combination of smaller power-of-two sized textures. If the
+ * requested texture size is larger than is supported by the hardware
+ * then the texture will be sliced into smaller textures that can be
+ * accessed by the hardware.
+ *
  * @max_waste is used as a threshold for recursively slicing the
- * right-most or bottom-most slices into smaller power-of-two sizes
- * until the wasted padding at the bottom and right of the
- * power-of-two textures is less than specified.
+ * right-most or bottom-most slices into smaller sizes until the
+ * 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 size
+ * is larger than is supported by the hardware but slicing is disabled
+ * the texture size would be too large to handle.</note>
  *
- * Returns: A newly allocated #CoglTexture2DSliced or if there was
- *          an error allocating any of the internal slices %NULL is
- *          returned and @error is updated.
+ * Returns: A new #CoglTexture2DSliced object with no storage
+ *          allocated yet.
  *
  * Since: 1.10
  * Stability: unstable
  */
 CoglTexture2DSliced *
 cogl_texture_2d_sliced_new_with_size (CoglContext *ctx,
-                                      unsigned int width,
-                                      unsigned int height,
+                                      int width,
+                                      int height,
                                       int max_waste,
-                                      CoglPixelFormat internal_format,
-                                      CoglError **error);
+                                      CoglPixelFormat internal_format);
 
 /**
  * cogl_is_texture_2d_sliced:
diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c
index 74ab1ab..7492e12 100644
--- a/cogl/cogl-texture-2d.c
+++ b/cogl/cogl-texture-2d.c
@@ -119,7 +119,7 @@ _cogl_texture_2d_create_base (CoglContext *ctx,
 
   tex_2d->is_foreign = FALSE;
 
-  tex_2d->format = internal_format;
+  tex_2d->internal_format = internal_format;
 
   ctx->driver_vtable->texture_2d_init (tex_2d);
 
@@ -130,27 +130,23 @@ CoglTexture2D *
 cogl_texture_2d_new_with_size (CoglContext *ctx,
                                int width,
                                int height,
-                               CoglPixelFormat internal_format,
-                               CoglError **error)
+                               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;
 
-  if (!_cogl_texture_2d_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 NULL;
-    }
+  return  _cogl_texture_2d_create_base (ctx,
+                                        width, height,
+                                        internal_format);
+}
 
-  return ctx->driver_vtable->texture_2d_new_with_size (ctx,
-                                                       width,
-                                                       height,
-                                                       internal_format,
-                                                       error);
+static CoglBool
+_cogl_texture_2d_allocate (CoglTexture *tex,
+                           CoglError **error)
+{
+  CoglContext *ctx = tex->context;
+  return ctx->driver_vtable->texture_2d_allocate (tex, error);
 }
 
 CoglTexture2D *
@@ -178,7 +174,6 @@ cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp,
                        "Failed to create texture 2d due to size/format"
                        " constraints");
       return NULL;
-
     }
 
   return ctx->driver_vtable->texture_2d_new_from_bitmap (bmp,
@@ -350,7 +345,11 @@ _cogl_texture_2d_copy_from_framebuffer (CoglTexture2D *tex_2d,
                                         int dst_y,
                                         int level)
 {
-  CoglContext *ctx = COGL_TEXTURE (tex_2d)->context;
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
+  CoglContext *ctx = tex->context;
+
+  /* Assert that the storage for this texture has been allocated */
+  cogl_texture_allocate (tex, NULL); /* (abort on error) */
 
   ctx->driver_vtable->texture_2d_copy_from_framebuffer (tex_2d,
                                                         src_x,
@@ -525,7 +524,7 @@ _cogl_texture_2d_get_data (CoglTexture *tex,
 static CoglPixelFormat
 _cogl_texture_2d_get_format (CoglTexture *tex)
 {
-  return COGL_TEXTURE_2D (tex)->format;
+  return COGL_TEXTURE_2D (tex)->internal_format;
 }
 
 static GLenum
@@ -550,6 +549,7 @@ static const CoglTextureVtable
 cogl_texture_2d_vtable =
   {
     TRUE, /* primitive */
+    _cogl_texture_2d_allocate,
     _cogl_texture_2d_set_region,
     _cogl_texture_2d_get_data,
     NULL, /* foreach_sub_texture_in_region */
diff --git a/cogl/cogl-texture-2d.h b/cogl/cogl-texture-2d.h
index fcd86f7..5a5ff2f 100644
--- a/cogl/cogl-texture-2d.h
+++ b/cogl/cogl-texture-2d.h
@@ -74,7 +74,6 @@ cogl_is_texture_2d (void *object);
  * @width: Width of the texture to allocate
  * @height: Height of the texture to allocate
  * @internal_format: The format of the texture
- * @error: A #CoglError for exceptions
  *
  * Allocates a low-level #CoglTexture2D texture that your GPU can
  * texture from directly. This is unlike sliced textures for example
@@ -82,15 +81,18 @@ cogl_is_texture_2d (void *object);
  * textures where Cogl has to modify texture coordinates before they
  * may be used by the GPU.
  *
+ * 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.
+ *
  * <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>
  *
- * Returns: A newly allocated #CoglTexture2D, or if the size is not
- *          supported (because it is too large or a non-power-of-two
- *          size that the hardware doesn't support) it will return
- *          %NULL and set @error.
+ * Returns: A new #CoglTexture2D object with no storage yet allocated.
  *
  * Since: 2.0
  */
@@ -98,8 +100,7 @@ CoglTexture2D *
 cogl_texture_2d_new_with_size (CoglContext *ctx,
                                int width,
                                int height,
-                               CoglPixelFormat internal_format,
-                               CoglError **error);
+                               CoglPixelFormat internal_format);
 
 /**
  * cogl_texture_2d_new_from_data:
diff --git a/cogl/cogl-texture-3d-private.h b/cogl/cogl-texture-3d-private.h
index 88f7026..22e572e 100644
--- a/cogl/cogl-texture-3d-private.h
+++ b/cogl/cogl-texture-3d-private.h
@@ -36,7 +36,7 @@ struct _CoglTexture3D
 
   /* The internal format of the texture represented as a
      CoglPixelFormat */
-  CoglPixelFormat format;
+  CoglPixelFormat internal_format;
   int depth;
   CoglBool auto_mipmap;
   CoglBool mipmaps_dirty;
diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c
index ab7c7e1..b40448a 100644
--- a/cogl/cogl-texture-3d.c
+++ b/cogl/cogl-texture-3d.c
@@ -95,7 +95,8 @@ _cogl_texture_3d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
 static void
 _cogl_texture_3d_free (CoglTexture3D *tex_3d)
 {
-  _cogl_delete_gl_texture (tex_3d->gl_texture);
+  if (tex_3d->gl_texture)
+    _cogl_delete_gl_texture (tex_3d->gl_texture);
 
   /* Chain up */
   _cogl_texture_free (COGL_TEXTURE (tex_3d));
@@ -122,6 +123,8 @@ _cogl_texture_3d_create_base (CoglContext *ctx,
 
   _cogl_texture_init (tex, ctx, width, height, &cogl_texture_3d_vtable);
 
+  tex_3d->gl_texture = 0;
+
   tex_3d->depth = depth;
   tex_3d->mipmaps_dirty = TRUE;
   tex_3d->auto_mipmap = TRUE;
@@ -135,7 +138,7 @@ _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->format = internal_format;
+  tex_3d->internal_format = internal_format;
 
   return _cogl_texture_3d_object_new (tex_3d);
 }
@@ -206,54 +209,66 @@ cogl_texture_3d_new_with_size (CoglContext *ctx,
                                int width,
                                int height,
                                int depth,
-                               CoglPixelFormat internal_format,
-                               CoglError **error)
+                               CoglPixelFormat internal_format)
 {
-  CoglTexture3D *tex_3d;
+  /* 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)
+{
+  CoglContext *ctx = tex->context;
+  CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex);
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
   GLenum gl_error;
-
-  /* Since no data, we need some internal format */
-  if (internal_format == COGL_PIXEL_FORMAT_ANY)
-    internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+  GLenum gl_texture;
 
   if (!_cogl_texture_3d_can_create (ctx,
-                                    width, height, depth,
-                                    internal_format,
+                                    tex->width,
+                                    tex->height,
+                                    tex_3d->depth,
+                                    tex_3d->internal_format,
                                     error))
-    return NULL;
-
-  internal_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
+    return FALSE;
 
-  tex_3d = _cogl_texture_3d_create_base (ctx,
-                                         width, height, depth,
-                                         internal_format);
+  ctx->driver_vtable->pixel_format_to_gl (ctx,
+                                          tex_3d->internal_format,
+                                          &gl_intformat,
+                                          &gl_format,
+                                          &gl_type);
 
-  tex_3d->gl_texture =
-    ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, internal_format);
+  gl_texture =
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, tex_3d->internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_3D,
-                                   tex_3d->gl_texture,
+                                   gl_texture,
                                    FALSE);
   /* Clear any GL errors */
   while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
     ;
 
   ctx->glTexImage3D (GL_TEXTURE_3D, 0, gl_intformat,
-                     width, height, depth, 0, gl_format, gl_type, NULL);
+                     tex->width, tex->height, tex_3d->depth,
+                     0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
     {
-      cogl_object_unref (tex_3d);
-      return NULL;
+      GE( ctx, glDeleteTextures (1, &gl_texture) );
+      return FALSE;
     }
 
-  return tex_3d;
+  tex_3d->gl_texture = gl_texture;
+  tex_3d->gl_format = gl_intformat;
+
+  return TRUE;
 }
 
 CoglTexture3D *
@@ -354,6 +369,8 @@ cogl_texture_3d_new_from_bitmap (CoglBitmap *bmp,
 
   cogl_object_unref (dst_bmp);
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_3d), TRUE);
+
   return tex_3d;
 }
 
@@ -628,7 +645,7 @@ _cogl_texture_3d_get_data (CoglTexture *tex,
 static CoglPixelFormat
 _cogl_texture_3d_get_format (CoglTexture *tex)
 {
-  return COGL_TEXTURE_3D (tex)->format;
+  return COGL_TEXTURE_3D (tex)->internal_format;
 }
 
 static GLenum
@@ -647,6 +664,7 @@ static const CoglTextureVtable
 cogl_texture_3d_vtable =
   {
     TRUE, /* primitive */
+    _cogl_texture_3d_allocate,
     _cogl_texture_3d_set_region,
     _cogl_texture_3d_get_data,
     NULL, /* foreach_sub_texture_in_region */
diff --git a/cogl/cogl-texture-3d.h b/cogl/cogl-texture-3d.h
index c2700ef..0483d47 100644
--- a/cogl/cogl-texture-3d.h
+++ b/cogl/cogl-texture-3d.h
@@ -55,18 +55,23 @@ typedef struct _CoglTexture3D CoglTexture3D;
  * @depth: depth of the texture in pixels.
  * @internal_format: the #CoglPixelFormat to use for the GPU
  *    storage of the texture.
- * @error: A CoglError return location.
  *
- * Creates a new Cogl 3D texture with the specified dimensions and
- * pixel format.
+ * Creates a new #CoglTexture3D texture with the specified dimensions
+ * and pixel format.
  *
- * 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.
+ * 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.
+ *
+ * <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: a new #CoglTexture3D object or
- *               %NULL on failure and an exception will be returned
- *               in @error.
+ * Returns: A new #CoglTexture3D object with no storage yet allocated.
  * Since: 1.10
  * Stability: Unstable
  */
@@ -75,8 +80,7 @@ cogl_texture_3d_new_with_size (CoglContext *context,
                                int width,
                                int height,
                                int depth,
-                               CoglPixelFormat  internal_format,
-                               CoglError **error);
+                               CoglPixelFormat internal_format);
 
 /**
  * cogl_texture_3d_new_from_data:
diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h
index 6ff4860..7e70e5e 100644
--- a/cogl/cogl-texture-private.h
+++ b/cogl/cogl-texture-private.h
@@ -60,6 +60,9 @@ struct _CoglTextureVtable
 
   CoglBool is_primitive;
 
+  CoglBool (* allocate) (CoglTexture *tex,
+                         CoglError **error);
+
   /* This should update the specified sub region of the texture with a
      sub region of the given bitmap. The bitmap is not converted
      before being passed so the implementation is expected to call
@@ -144,6 +147,7 @@ struct _CoglTexture
   int max_level;
   int width;
   int height;
+  CoglBool allocated;
   const CoglTextureVtable *vtable;
 };
 
@@ -321,4 +325,8 @@ _cogl_texture_get_level_size (CoglTexture *texture,
                               int *height,
                               int *depth);
 
+void
+_cogl_texture_set_allocated (CoglTexture *texture,
+                             CoglBool allocated);
+
 #endif /* __COGL_TEXTURE_PRIVATE_H */
diff --git a/cogl/cogl-texture-rectangle-private.h b/cogl/cogl-texture-rectangle-private.h
index e6592a5..fe4dfe8 100644
--- a/cogl/cogl-texture-rectangle-private.h
+++ b/cogl/cogl-texture-rectangle-private.h
@@ -34,7 +34,7 @@ struct _CoglTextureRectangle
 
   /* The internal format of the texture represented as a
      CoglPixelFormat */
-  CoglPixelFormat format;
+  CoglPixelFormat internal_format;
 
   /* TODO: factor out these OpenGL specific members into some form
    * of driver private state. */
diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c
index ee4cfcf..f8d0811 100644
--- a/cogl/cogl-texture-rectangle.c
+++ b/cogl/cogl-texture-rectangle.c
@@ -102,7 +102,7 @@ _cogl_texture_rectangle_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex,
 static void
 _cogl_texture_rectangle_free (CoglTextureRectangle *tex_rect)
 {
-  if (!tex_rect->is_foreign)
+  if (!tex_rect->is_foreign && tex_rect->gl_texture)
     _cogl_delete_gl_texture (tex_rect->gl_texture);
 
   /* Chain up */
@@ -173,6 +173,8 @@ _cogl_texture_rectangle_create_base (CoglContext *ctx,
 
   _cogl_texture_init (tex, ctx, width, height, &cogl_texture_rectangle_vtable);
 
+  tex_rect->gl_texture = 0;
+
   /* We default to GL_LINEAR for both filters */
   tex_rect->gl_legacy_texobj_min_filter = GL_LINEAR;
   tex_rect->gl_legacy_texobj_mag_filter = GL_LINEAR;
@@ -181,7 +183,7 @@ _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->format = internal_format;
+  tex_rect->internal_format = internal_format;
 
   return _cogl_texture_rectangle_object_new (tex_rect);
 }
@@ -194,36 +196,59 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
                                       CoglError **error)
 {
   CoglTextureRectangle *tex_rect;
-  GLenum gl_intformat;
-  GLenum gl_format;
-  GLenum gl_type;
-  GLenum gl_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_rectangle_can_create (ctx,
-                                           width, height,
-                                           internal_format, error))
-    return NULL;
+  tex_rect =_cogl_texture_rectangle_create_base (ctx,
+                                                 width, height,
+                                                 internal_format);
 
-  internal_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
+  /* XXX: This api has been changed for Cogl 2.0 on the master branch
+   * to not take a CoglError to allow the storage to be allocated
+   * lazily but since Mutter uses this api we are currently
+   * maintaining the semantics of immediately allocating the storage
+   */
+  if (!cogl_texture_allocate (COGL_TEXTURE (tex_rect), error))
+    {
+      cogl_object_unref (tex_rect);
+      return NULL;
+    }
+  return tex_rect;
+}
 
-  tex_rect = _cogl_texture_rectangle_create_base (ctx,
-                                                  width, height,
-                                                  internal_format);
+static CoglBool
+_cogl_texture_rectangle_allocate (CoglTexture *tex,
+                                  CoglError **error)
+{
+  CoglContext *ctx = tex->context;
+  CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex);
+  GLenum gl_intformat;
+  GLenum gl_format;
+  GLenum gl_type;
+  GLenum gl_error;
+  GLenum gl_texture;
 
-  tex_rect->gl_texture =
+  if (!_cogl_texture_rectangle_can_create (ctx,
+                                           tex->width,
+                                           tex->height,
+                                           tex_rect->internal_format,
+                                           error))
+    return FALSE;
+
+  ctx->driver_vtable->pixel_format_to_gl (ctx,
+                                          tex_rect->internal_format,
+                                          &gl_intformat,
+                                          &gl_format,
+                                          &gl_type);
+
+  gl_texture =
     ctx->texture_driver->gen (ctx,
                               GL_TEXTURE_RECTANGLE_ARB,
-                              internal_format);
+                              tex_rect->internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_RECTANGLE_ARB,
-                                   tex_rect->gl_texture,
+                                   gl_texture,
                                    tex_rect->is_foreign);
 
   /* Clear any GL errors */
@@ -231,15 +256,18 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
     ;
 
   ctx->glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, gl_intformat,
-                     width, height, 0, gl_format, gl_type, NULL);
+                     tex->width, tex->height, 0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
     {
-      cogl_object_unref (tex_rect);
-      return NULL;
+      GE( ctx, glDeleteTextures (1, &gl_texture) );
+      return FALSE;
     }
 
-  return tex_rect;
+  tex_rect->gl_texture = gl_texture;
+  tex_rect->gl_format = gl_intformat;
+
+  return TRUE;
 }
 
 CoglTextureRectangle *
@@ -308,6 +336,8 @@ cogl_texture_rectangle_new_from_bitmap (CoglBitmap *bmp,
 
   cogl_object_unref (dst_bmp);
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_rect), TRUE);
+
   return tex_rect;
 }
 
@@ -424,7 +454,7 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
   /* Setup bitmap info */
   tex_rect->is_foreign = TRUE;
 
-  tex_rect->format = format;
+  tex_rect->internal_format = format;
 
   tex_rect->gl_texture = gl_handle;
   tex_rect->gl_format = gl_int_format;
@@ -433,6 +463,8 @@ cogl_texture_rectangle_new_from_foreign (CoglContext *ctx,
   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);
+
   return tex_rect;
 }
 
@@ -624,7 +656,7 @@ _cogl_texture_rectangle_get_data (CoglTexture *tex,
 static CoglPixelFormat
 _cogl_texture_rectangle_get_format (CoglTexture *tex)
 {
-  return COGL_TEXTURE_RECTANGLE (tex)->format;
+  return COGL_TEXTURE_RECTANGLE (tex)->internal_format;
 }
 
 static GLenum
@@ -649,6 +681,7 @@ static const CoglTextureVtable
 cogl_texture_rectangle_vtable =
   {
     TRUE, /* primitive */
+    _cogl_texture_rectangle_allocate,
     _cogl_texture_rectangle_set_region,
     _cogl_texture_rectangle_get_data,
     NULL, /* foreach_sub_texture_in_region */
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index 28d1f4c..1b97648 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -139,6 +139,7 @@ _cogl_texture_init (CoglTexture *texture,
   texture->max_level = 0;
   texture->width = width;
   texture->height = height;
+  texture->allocated = FALSE;
   texture->vtable = vtable;
   texture->framebuffers = NULL;
 }
@@ -440,6 +441,20 @@ _cogl_texture_get_type (CoglTexture *texture)
 void
 _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags)
 {
+  /* Assert that the storage for the texture exists already if we're
+   * about to reference it for painting.
+   *
+   * Note: we abort on error here since it's a bit late to do anything
+   * about it if we fail to allocate the texture and the app could
+   * have explicitly allocated the texture earlier to handle problems
+   * gracefully.
+   *
+   * XXX: Maybe it could even be considered a programmer error if the
+   * texture hasn't been allocated by this point since it implies we
+   * are abount to paint with undefined texture contents?
+   */
+  cogl_texture_allocate (texture, NULL);
+
   texture->vtable->pre_paint (texture, flags);
 }
 
@@ -465,10 +480,12 @@ _cogl_texture_set_region_from_bitmap (CoglTexture *texture,
                             >= width, FALSE);
   _COGL_RETURN_VAL_IF_FAIL ((cogl_bitmap_get_height (bmp) - src_y)
                             >= height, FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (width > 0, FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (height > 0, FALSE);
 
-  /* Shortcut out early if the image is empty */
-  if (width == 0 || height == 0)
-    return TRUE;
+  /* Assert that the storage for this texture has been allocated */
+  if (!cogl_texture_allocate (texture, error))
+    return FALSE;
 
   /* Note that we don't prepare the bitmap for upload here because
      some backends may be internally using a different format for the
@@ -897,10 +914,12 @@ get_texture_bits_via_offscreen (CoglTexture    *texture,
                                        COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL,
                                        0);
 
-  if (offscreen == NULL)
-    return FALSE;
-
   framebuffer = COGL_FRAMEBUFFER (offscreen);
+  if (!cogl_framebuffer_allocate (framebuffer, &ignore_error))
+    {
+      cogl_error_free (ignore_error);
+      return FALSE;
+    }
 
   bitmap = cogl_bitmap_new_for_data (ctx,
                                      width, height,
@@ -1390,3 +1409,22 @@ _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans,
 	}
     }
 }
+
+void
+_cogl_texture_set_allocated (CoglTexture *texture,
+                             CoglBool allocated)
+{
+  texture->allocated = allocated;
+}
+
+CoglBool
+cogl_texture_allocate (CoglTexture *texture,
+                       CoglError **error)
+{
+  if (texture->allocated)
+    return TRUE;
+
+  texture->allocated = texture->vtable->allocate (texture, error);
+
+  return texture->allocated;
+}
diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h
index 17658ea..b744bab 100644
--- a/cogl/cogl-texture.h
+++ b/cogl/cogl-texture.h
@@ -112,7 +112,14 @@ uint32_t cogl_texture_error_quark (void);
  *
  * Creates a new #CoglTexture with the specified dimensions and pixel format.
  *
- * Return value: A newly created #CoglTexture or %NULL on failure
+ * The storage for the texture is not necesarily created before this
+ * function returns. The storage can be explicitly allocated using
+ * cogl_texture_allocate() or preferably you can let Cogl
+ * automatically allocate the storage lazily when uploading data when
+ * Cogl may know more about how the texture will be used and can
+ * optimize how it is allocated.
+ *
+ * Return value: A newly created #CoglTexture
  *
  * Since: 0.8
  */
@@ -517,6 +524,28 @@ cogl_texture_unref (void *texture) G_GNUC_DEPRECATED;
 
 #endif /* COGL_DISABLE_DEPRECATED */
 
+/**
+ * cogl_texture_allocate:
+ * @texture: A #CoglTexture
+ * @error: A #CoglError to return exceptional errors or %NULL
+ *
+ * Explicitly allocates the storage for the given @texture which
+ * allows you to be sure that there is enough memory for the
+ * texture and if not then the error can be handled gracefully.
+ *
+ * <note>Normally applications don't need to use this api directly
+ * since the texture will be implicitly allocated when data is set on
+ * the texture, or if the texture is attached to a #CoglOffscreen
+ * framebuffer and rendered too.</note>
+ *
+ * Return value: %TRUE if the texture was successfully allocated,
+ *               otherwise %FALSE and @error will be updated if it
+ *               wasn't %NULL.
+ */
+CoglBool
+cogl_texture_allocate (CoglTexture *texture,
+                       CoglError **error);
+
 COGL_END_DECLS
 
 #endif /* __COGL_TEXTURE_H__ */
diff --git a/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/driver/gl/cogl-framebuffer-gl.c
index a0a50cf..043d047 100644
--- a/cogl/driver/gl/cogl-framebuffer-gl.c
+++ b/cogl/driver/gl/cogl-framebuffer-gl.c
@@ -382,8 +382,7 @@ create_depth_texture (CoglContext *ctx,
 
   depth_texture =  cogl_texture_2d_new_with_size (ctx,
                                                   width, height,
-                                                  format,
-                                                  NULL);
+                                                  format);
 
   return COGL_TEXTURE (depth_texture);
 }
@@ -712,14 +711,14 @@ _cogl_offscreen_gl_allocate (CoglOffscreen *offscreen,
                               offscreen->texture_level_width,
                               offscreen->texture_level_height);
 
-      if (offscreen->depth_texture)
-        _cogl_texture_associate_framebuffer (offscreen->depth_texture, fb);
-      else
+      if (!cogl_texture_allocate (offscreen->depth_texture, error))
         {
-          _cogl_set_error (error, COGL_FRAMEBUFFER_ERROR,
-                           COGL_FRAMEBUFFER_ERROR_ALLOCATE,
-                           "Failed to allocate depth texture for framebuffer");
+          cogl_object_unref (offscreen->depth_texture);
+          offscreen->depth_texture = NULL;
+          return FALSE;
         }
+
+      _cogl_texture_associate_framebuffer (offscreen->depth_texture, fb);
     }
 
   /* XXX: The framebuffer_object spec isn't clear in defining whether attaching
diff --git a/cogl/driver/gl/cogl-texture-2d-gl-private.h b/cogl/driver/gl/cogl-texture-2d-gl-private.h
index d77f460..c71908f 100644
--- a/cogl/driver/gl/cogl-texture-2d-gl-private.h
+++ b/cogl/driver/gl/cogl-texture-2d-gl-private.h
@@ -44,12 +44,9 @@ _cogl_texture_2d_gl_can_create (CoglContext *ctx,
 void
 _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d);
 
-CoglTexture2D *
-_cogl_texture_2d_gl_new_with_size (CoglContext *ctx,
-                                   int width,
-                                   int height,
-                                   CoglPixelFormat internal_format,
-                                   CoglError **error);
+CoglBool
+_cogl_texture_2d_gl_allocate (CoglTexture *tex,
+                              CoglError **error);
 
 CoglTexture2D *
 _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp,
diff --git a/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/driver/gl/cogl-texture-2d-gl.c
index 4f57eed..8bcfb93 100644
--- a/cogl/driver/gl/cogl-texture-2d-gl.c
+++ b/cogl/driver/gl/cogl-texture-2d-gl.c
@@ -43,7 +43,7 @@
 void
 _cogl_texture_2d_gl_free (CoglTexture2D *tex_2d)
 {
-  if (!tex_2d->is_foreign)
+  if (!tex_2d->is_foreign && tex_2d->gl_texture)
     _cogl_delete_gl_texture (tex_2d->gl_texture);
 }
 
@@ -79,6 +79,8 @@ _cogl_texture_2d_gl_can_create (CoglContext *ctx,
 void
 _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d)
 {
+  tex_2d->gl_texture = 0;
+
   /* We default to GL_LINEAR for both filters */
   tex_2d->gl_legacy_texobj_min_filter = GL_LINEAR;
   tex_2d->gl_legacy_texobj_mag_filter = GL_LINEAR;
@@ -88,36 +90,43 @@ _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d)
   tex_2d->gl_legacy_texobj_wrap_mode_t = GL_FALSE;
 }
 
-CoglTexture2D *
-_cogl_texture_2d_gl_new_with_size (CoglContext *ctx,
-                                   int width,
-                                   int height,
-                                   CoglPixelFormat internal_format,
-                                   CoglError **error)
+CoglBool
+_cogl_texture_2d_gl_allocate (CoglTexture *tex,
+                              CoglError **error)
 {
-  CoglTexture2D *tex_2d;
+  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 = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
+  if (!_cogl_texture_2d_gl_can_create (ctx,
+                                       tex->width,
+                                       tex->height,
+                                       tex_2d->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;
+    }
 
-  tex_2d = _cogl_texture_2d_create_base (ctx,
-                                         width, height,
-                                         internal_format);
+  ctx->driver_vtable->pixel_format_to_gl (ctx,
+                                          tex_2d->internal_format,
+                                          &gl_intformat,
+                                          &gl_format,
+                                          &gl_type);
 
-  tex_2d->gl_texture =
-    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format);
+  gl_texture =
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, tex_2d->internal_format);
 
   tex_2d->gl_internal_format = gl_intformat;
 
   _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
-                                   tex_2d->gl_texture,
+                                   gl_texture,
                                    tex_2d->is_foreign);
 
   /* Clear any GL errors */
@@ -125,15 +134,18 @@ _cogl_texture_2d_gl_new_with_size (CoglContext *ctx,
     ;
 
   ctx->glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat,
-                     width, height, 0, gl_format, gl_type, NULL);
+                     tex->width, tex->height, 0, gl_format, gl_type, NULL);
 
   if (_cogl_gl_util_catch_out_of_memory (ctx, error))
     {
-      cogl_object_unref (tex_2d);
-      return NULL;
+      GE( ctx, glDeleteTextures (1, &gl_texture) );
+      return FALSE;
     }
 
-  return tex_2d;
+  tex_2d->gl_texture = gl_texture;
+  tex_2d->gl_internal_format = gl_intformat;
+
+  return TRUE;
 }
 
 CoglTexture2D *
@@ -213,6 +225,8 @@ _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp,
 
   cogl_object_unref (dst_bmp);
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE);
+
   return tex_2d;
 
 }
@@ -249,9 +263,12 @@ _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;
     }
 
+  _cogl_texture_set_allocated (COGL_TEXTURE (tex_2d), TRUE);
+
   return tex_2d;
 }
 #endif
@@ -442,8 +459,6 @@ cogl_texture_2d_new_from_foreign (CoglContext *ctx,
   tex_2d->is_foreign = TRUE;
   tex_2d->mipmaps_dirty = TRUE;
 
-  tex_2d->format = format;
-
   tex_2d->gl_texture = gl_handle;
   tex_2d->gl_internal_format = gl_int_format;
 
@@ -451,6 +466,8 @@ cogl_texture_2d_new_from_foreign (CoglContext *ctx,
   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);
+
   return tex_2d;
 }
 
@@ -465,7 +482,8 @@ _cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d,
                                            int dst_y,
                                            int level)
 {
-  CoglContext *ctx = COGL_TEXTURE (tex_2d)->context;
+  CoglTexture *tex = COGL_TEXTURE (tex_2d);
+  CoglContext *ctx = tex->context;
 
   /* Make sure the current framebuffers are bound, though we don't need to
    * flush the clip state here since we aren't going to draw to the
diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c
index bd12c81..141371e 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -638,7 +638,7 @@ _cogl_driver_gl =
     _cogl_texture_2d_gl_free,
     _cogl_texture_2d_gl_can_create,
     _cogl_texture_2d_gl_init,
-    _cogl_texture_2d_gl_new_with_size,
+    _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,
diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c
index b4d5efd..494b14e 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -380,7 +380,7 @@ _cogl_driver_gles =
     _cogl_texture_2d_gl_free,
     _cogl_texture_2d_gl_can_create,
     _cogl_texture_2d_gl_init,
-    _cogl_texture_2d_gl_new_with_size,
+    _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,
diff --git a/cogl/driver/nop/cogl-driver-nop.c b/cogl/driver/nop/cogl-driver-nop.c
index 40b101b..7a782f9 100644
--- a/cogl/driver/nop/cogl-driver-nop.c
+++ b/cogl/driver/nop/cogl-driver-nop.c
@@ -68,7 +68,7 @@ _cogl_driver_nop =
     _cogl_texture_2d_nop_free,
     _cogl_texture_2d_nop_can_create,
     _cogl_texture_2d_nop_init,
-    _cogl_texture_2d_nop_new_with_size,
+    _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,
diff --git a/cogl/driver/nop/cogl-texture-2d-nop-private.h b/cogl/driver/nop/cogl-texture-2d-nop-private.h
index 2e79e66..1aa558a 100644
--- a/cogl/driver/nop/cogl-texture-2d-nop-private.h
+++ b/cogl/driver/nop/cogl-texture-2d-nop-private.h
@@ -44,12 +44,9 @@ _cogl_texture_2d_nop_can_create (CoglContext *ctx,
 void
 _cogl_texture_2d_nop_init (CoglTexture2D *tex_2d);
 
-CoglTexture2D *
-_cogl_texture_2d_nop_new_with_size (CoglContext *ctx,
-                                    int width,
-                                    int height,
-                                    CoglPixelFormat internal_format,
-                                    CoglError **error);
+CoglBool
+_cogl_texture_2d_nop_allocate (CoglTexture *tex,
+                               CoglError **error);
 
 CoglTexture2D *
 _cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp,
diff --git a/cogl/driver/nop/cogl-texture-2d-nop.c b/cogl/driver/nop/cogl-texture-2d-nop.c
index 948d620..5831e27 100644
--- a/cogl/driver/nop/cogl-texture-2d-nop.c
+++ b/cogl/driver/nop/cogl-texture-2d-nop.c
@@ -55,16 +55,11 @@ _cogl_texture_2d_nop_init (CoglTexture2D *tex_2d)
 {
 }
 
-CoglTexture2D *
-_cogl_texture_2d_nop_new_with_size (CoglContext *ctx,
-                                    int width,
-                                    int height,
-                                    CoglPixelFormat internal_format,
-                                    CoglError **error)
+CoglBool
+_cogl_texture_2d_nop_allocate (CoglTexture *tex,
+                               CoglError **error)
 {
-  return _cogl_texture_2d_create_base (ctx,
-                                       width, height,
-                                       internal_format);
+  return TRUE;
 }
 
 CoglTexture2D *
@@ -72,11 +67,10 @@ _cogl_texture_2d_nop_new_from_bitmap (CoglBitmap *bmp,
                                       CoglPixelFormat internal_format,
                                       CoglError **error)
 {
-  return _cogl_texture_2d_nop_new_with_size (_cogl_bitmap_get_context (bmp),
-                                             cogl_bitmap_get_width (bmp),
-                                             cogl_bitmap_get_height (bmp),
-                                             internal_format,
-                                             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)
diff --git a/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/winsys/cogl-texture-pixmap-x11.c
index 2b66414..71d00ea 100644
--- a/cogl/winsys/cogl-texture-pixmap-x11.c
+++ b/cogl/winsys/cogl-texture-pixmap-x11.c
@@ -361,9 +361,18 @@ cogl_texture_pixmap_x11_new (CoglContext *ctxt,
   if (!tex_pixmap->use_winsys_texture)
     tex_pixmap->winsys = NULL;
 
+  _cogl_texture_set_allocated (tex, TRUE);
+
   return _cogl_texture_pixmap_x11_object_new (tex_pixmap);
 }
 
+static CoglBool
+_cogl_texture_pixmap_x11_allocate (CoglTexture *tex,
+                                   CoglError **error)
+{
+  return TRUE;
+}
+
 /* Tries to allocate enough shared mem to handle a full size
  * update size of the X Pixmap. */
 static void
@@ -1001,6 +1010,7 @@ static const CoglTextureVtable
 cogl_texture_pixmap_x11_vtable =
   {
     FALSE, /* not primitive */
+    _cogl_texture_pixmap_x11_allocate,
     _cogl_texture_pixmap_x11_set_region,
     _cogl_texture_pixmap_x11_get_data,
     _cogl_texture_pixmap_x11_foreach_sub_texture_in_region,
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 2bfa190..a9f88e0 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -1980,17 +1980,17 @@ _cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap,
             cogl_texture_2d_new_with_size (ctx,
                                            tex->width,
                                            tex->height,
-                                           texture_format,
-                                           NULL));
+                                           texture_format));
 
-          if (glx_tex_pixmap->glx_tex)
+          if (cogl_texture_allocate (glx_tex_pixmap->glx_tex, &error))
             COGL_NOTE (TEXTURE_PIXMAP, "Created a texture 2d for %p",
                        tex_pixmap);
           else
             {
               COGL_NOTE (TEXTURE_PIXMAP, "Falling back for %p because a "
-                         "texture 2d could not be created",
-                         tex_pixmap);
+                         "texture 2d could not be created: %s",
+                         tex_pixmap, error->message);
+              cogl_error_free (error);
               free_glx_pixmap (ctx, glx_tex_pixmap);
               return FALSE;
             }
diff --git a/tests/conform/test-gles2-context.c b/tests/conform/test-gles2-context.c
index 9a3c8b6..9e66ab0 100644
--- a/tests/conform/test-gles2-context.c
+++ b/tests/conform/test-gles2-context.c
@@ -27,8 +27,7 @@ test_push_pop_single_context (void)
     cogl_texture_2d_new_with_size (test_ctx,
                                    cogl_framebuffer_get_width (test_fb),
                                    cogl_framebuffer_get_height (test_fb),
-                                   COGL_PIXEL_FORMAT_ANY,
-                                   NULL));
+                                   COGL_PIXEL_FORMAT_ANY));
   offscreen = cogl_offscreen_new_to_texture (offscreen_texture);
 
   pipeline = cogl_pipeline_new (test_ctx);
@@ -152,8 +151,7 @@ create_gles2_context (CoglTexture **offscreen_texture,
     cogl_texture_2d_new_with_size (test_ctx,
                                    cogl_framebuffer_get_width (test_fb),
                                    cogl_framebuffer_get_height (test_fb),
-                                   COGL_PIXEL_FORMAT_ANY,
-                                   NULL));
+                                   COGL_PIXEL_FORMAT_ANY));
   *offscreen = cogl_offscreen_new_to_texture (*offscreen_texture);
 
   *pipeline = cogl_pipeline_new (test_ctx);
diff --git a/tests/conform/test-offscreen.c b/tests/conform/test-offscreen.c
index 26578d5..5a4863b 100644
--- a/tests/conform/test-offscreen.c
+++ b/tests/conform/test-offscreen.c
@@ -45,8 +45,7 @@ test_paint (TestState *state)
   tex_2d = cogl_texture_2d_new_with_size (test_ctx,
                                           state->fb_width,
                                           state->fb_height,
-                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                          NULL);
+                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE);
   tex = COGL_TEXTURE (tex_2d);
 
   offscreen = cogl_offscreen_new_to_texture (tex);
@@ -127,8 +126,7 @@ test_flush (TestState *state)
 
       tex_2d = cogl_texture_2d_new_with_size (test_ctx,
                                               16, 16, /* width/height */
-                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                              NULL);
+                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE);
       tex = COGL_TEXTURE (tex_2d);
 
       offscreen = cogl_offscreen_new_to_texture (tex);
diff --git a/tests/conform/test-utils.c b/tests/conform/test-utils.c
index 684327d..9f3a4b4 100644
--- a/tests/conform/test-utils.c
+++ b/tests/conform/test-utils.c
@@ -122,11 +122,7 @@ test_utils_init (TestFlags requirement_flags,
       CoglOffscreen *offscreen;
       CoglTexture2D *tex = cogl_texture_2d_new_with_size (test_ctx,
                                                           FB_WIDTH, FB_HEIGHT,
-                                                          COGL_PIXEL_FORMAT_ANY,
-                                                          &error);
-      if (!tex)
-        g_critical ("Failed to allocate texture: %s", error->message);
-
+                                                          COGL_PIXEL_FORMAT_ANY);
       offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (tex));
       test_fb = COGL_FRAMEBUFFER (offscreen);
     }



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