[Cogl] [PATCH] Allow lazy texture storage allocation

Robert Bragg robert at sixbynine.org
Thu Dec 6 03:54:16 PST 2012


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

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 gracefully
catch any errors with texture allocation but if this function isn't used
explicitly then Cogl will implicitly ensure textures are allocated
before the storage is needed.
---
 cogl-pango/cogl-pango-glyph-cache.c           |    9 +-
 cogl/cogl-atlas-texture-private.h             |    3 +-
 cogl/cogl-atlas-texture.c                     |   90 ++++++--
 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                 |  286 ++++++++++++++-----------
 cogl/cogl-texture-2d-sliced.h                 |    4 +-
 cogl/cogl-texture-2d.c                        |   31 ++-
 cogl/cogl-texture-3d-private.h                |    2 +-
 cogl/cogl-texture-3d.c                        |   53 +++--
 cogl/cogl-texture-private.h                   |    8 +
 cogl/cogl-texture-rectangle-private.h         |    2 +-
 cogl/cogl-texture-rectangle.c                 |   56 +++--
 cogl/cogl-texture.c                           |   50 ++++-
 cogl/cogl-texture.h                           |   22 ++
 cogl/driver/gl/cogl-texture-2d-gl-private.h   |    9 +-
 cogl/driver/gl/cogl-texture-2d-gl.c           |   49 ++---
 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 +
 28 files changed, 522 insertions(+), 328 deletions(-)

diff --git a/cogl-pango/cogl-pango-glyph-cache.c b/cogl-pango/cogl-pango-glyph-cache.c
index 47234ee..27cdcbe 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 f717571..f37a7cc 100644
--- a/cogl/cogl-atlas-texture.c
+++ b/cogl/cogl-atlas-texture.c
@@ -656,32 +656,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);
 
@@ -690,7 +718,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;
     }
 
@@ -708,12 +739,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);
@@ -727,20 +773,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 *
@@ -767,14 +816,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;
     }
 
@@ -844,6 +893,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-blit.c b/cogl/cogl-blit.c
index 14f2279..6aaaf7d 100644
--- a/cogl/cogl-blit.c
+++ b/cogl/cogl-blit.c
@@ -48,16 +48,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;
     }
@@ -147,6 +146,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 */
@@ -157,24 +157,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;
@@ -219,6 +220,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))
@@ -227,12 +229,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 5a49599..c7f4bd9 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 457b3dd..e6d0082 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -297,7 +297,7 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
 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 abb859f..d99cda1 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -48,6 +48,7 @@
 #include "cogl-primitives-private.h"
 #include "cogl-path-private.h"
 #include "cogl-error-private.h"
+#include "cogl-texture-gl-private.h"
 
 extern CoglObjectClass _cogl_onscreen_class;
 
@@ -567,48 +568,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);
@@ -686,7 +663,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 6b5ef1f..cb9da39 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 8d155d0..9fc6971 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,144 @@ _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)
 {
-  CoglTexture2DSliced   *tex_2ds;
+  CoglTexture2DSliced *tex_2ds;
 
   /* 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,
+                                          error))
     {
+      /* NB: we may fail to find any suitable slicing geometry for the
+       * given texture size. */
       _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;
+      return FALSE;
     }
 
+  /* 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);
+  CoglTexture2D **slice_textures;
+  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);
+
+  slice_textures = (CoglTexture2D **) tex_2ds->slice_textures->data;
+
+  /* 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)
+        {
+          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_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 +989,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 +1006,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 +1105,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 +1133,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 +1166,14 @@ _cogl_texture_2d_sliced_is_sliced (CoglTexture *tex)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
 
-  if (tex_2ds->slice_textures == NULL)
-    return FALSE;
+  _COGL_RETURN_VAL_IF_FAIL (tex_2ds->slice_x_spans, TRUE);
+  _COGL_RETURN_VAL_IF_FAIL (tex_2ds->slice_y_spans, TRUE);
 
-  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 +1278,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 +1299,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 +1318,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 +1376,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 +1386,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 +1404,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 363d6d8..d8469bc 100644
--- a/cogl/cogl-texture-2d-sliced.h
+++ b/cogl/cogl-texture-2d-sliced.h
@@ -91,8 +91,8 @@ typedef struct _CoglTexture2DSliced CoglTexture2DSliced;
  */
 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);
diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c
index 74ab1ab..e488821 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);
 
@@ -137,7 +137,8 @@ cogl_texture_2d_new_with_size (CoglContext *ctx,
   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))
+  if (!_cogl_texture_2d_can_create (ctx, width, height,
+                                    internal_format))
     {
       _cogl_set_error (error, COGL_TEXTURE_ERROR,
                        COGL_TEXTURE_ERROR_SIZE,
@@ -146,11 +147,17 @@ cogl_texture_2d_new_with_size (CoglContext *ctx,
       return NULL;
     }
 
-  return ctx->driver_vtable->texture_2d_new_with_size (ctx,
-                                                       width,
-                                                       height,
-                                                       internal_format,
-                                                       error);
+  return  _cogl_texture_2d_create_base (ctx,
+                                        width, height,
+                                        internal_format);
+}
+
+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 +185,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 +356,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 +535,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 +560,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-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..fc54579 100644
--- a/cogl/cogl-texture-3d.c
+++ b/cogl/cogl-texture-3d.c
@@ -135,7 +135,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);
 }
@@ -209,12 +209,6 @@ cogl_texture_3d_new_with_size (CoglContext *ctx,
                                CoglPixelFormat internal_format,
                                CoglError **error)
 {
-  CoglTexture3D *tex_3d;
-  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;
@@ -225,18 +219,30 @@ cogl_texture_3d_new_with_size (CoglContext *ctx,
                                     error))
     return NULL;
 
-  internal_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
+  return _cogl_texture_3d_create_base (ctx,
+                                       width, height, depth,
+                                       internal_format);
+}
 
-  tex_3d = _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;
+
+  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);
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_3D, tex_3d->internal_format);
   _cogl_bind_gl_texture_transient (GL_TEXTURE_3D,
                                    tex_3d->gl_texture,
                                    FALSE);
@@ -245,15 +251,13 @@ cogl_texture_3d_new_with_size (CoglContext *ctx,
     ;
 
   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;
-    }
+    return FALSE;
 
-  return tex_3d;
+  return TRUE;
 }
 
 CoglTexture3D *
@@ -354,6 +358,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 +634,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 +653,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-private.h b/cogl/cogl-texture-private.h
index 0e7a15e..92ddae2 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;
 };
 
@@ -291,4 +295,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..6db60c0 100644
--- a/cogl/cogl-texture-rectangle.c
+++ b/cogl/cogl-texture-rectangle.c
@@ -181,7 +181,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);
 }
@@ -193,12 +193,6 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
                                       CoglPixelFormat internal_format,
                                       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;
@@ -208,20 +202,32 @@ cogl_texture_rectangle_new_with_size (CoglContext *ctx,
                                            internal_format, error))
     return NULL;
 
-  internal_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
+  return _cogl_texture_rectangle_create_base (ctx,
+                                              width, height,
+                                              internal_format);
+}
 
-  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;
+
+  ctx->driver_vtable->pixel_format_to_gl (ctx,
+                                          tex_rect->internal_format,
+                                          &gl_intformat,
+                                          &gl_format,
+                                          &gl_type);
 
   tex_rect->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,
                                    tex_rect->is_foreign);
@@ -231,15 +237,12 @@ 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;
-    }
+    return FALSE;
 
-  return tex_rect;
+  return TRUE;
 }
 
 CoglTextureRectangle *
@@ -308,6 +311,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 +429,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 +438,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 +631,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 +656,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 4803388..ef795c0 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -108,6 +108,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;
 }
@@ -383,6 +384,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);
 }
 
@@ -408,10 +423,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
@@ -767,10 +784,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,
@@ -1248,3 +1267,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 0b5475f..8ede133 100644
--- a/cogl/cogl-texture.h
+++ b/cogl/cogl-texture.h
@@ -525,6 +525,28 @@ cogl_texture_set_region_from_bitmap (CoglTexture *texture,
                                      int level,
                                      CoglError **error);
 
+/**
+ * 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-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 267b4d2..11d7169 100644
--- a/cogl/driver/gl/cogl-texture-2d-gl.c
+++ b/cogl/driver/gl/cogl-texture-2d-gl.c
@@ -88,31 +88,25 @@ _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;
 
-  internal_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                                            internal_format,
-                                                            &gl_intformat,
-                                                            &gl_format,
-                                                            &gl_type);
-
-  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);
+    ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, tex_2d->internal_format);
 
   tex_2d->gl_internal_format = gl_intformat;
 
@@ -125,15 +119,12 @@ _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;
-    }
+    return FALSE;
 
-  return tex_2d;
+  return TRUE;
 }
 
 CoglTexture2D *
@@ -213,6 +204,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 +242,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 +438,6 @@ cogl_texture_2d_gl_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 +445,8 @@ cogl_texture_2d_gl_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 +461,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 eddce23..c1ab3d2 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -605,7 +605,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 44ac366..ad07c0a 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -352,7 +352,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 b3536ff..26ae026 100644
--- a/cogl/driver/nop/cogl-driver-nop.c
+++ b/cogl/driver/nop/cogl-driver-nop.c
@@ -67,7 +67,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 6823074..8ad6f3a 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
@@ -1004,6 +1013,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,
-- 
1.7.7.6



More information about the Cogl mailing list