diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index dcf596610..440b6ab7e 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -146,6 +146,8 @@ cogl_sources_c = \ $(srcdir)/cogl-texture-rectangle.c \ $(srcdir)/cogl-rectangle-map.h \ $(srcdir)/cogl-rectangle-map.c \ + $(srcdir)/cogl-atlas.h \ + $(srcdir)/cogl-atlas.c \ $(srcdir)/cogl-atlas-texture-private.h \ $(srcdir)/cogl-atlas-texture.c \ $(srcdir)/cogl-spans.h \ diff --git a/clutter/cogl/cogl/cogl-atlas-texture-private.h b/clutter/cogl/cogl/cogl-atlas-texture-private.h index c2c9fbd88..eaa6a84e3 100644 --- a/clutter/cogl/cogl/cogl-atlas-texture-private.h +++ b/clutter/cogl/cogl/cogl-atlas-texture-private.h @@ -27,6 +27,7 @@ #include "cogl-handle.h" #include "cogl-texture-private.h" #include "cogl-rectangle-map.h" +#include "cogl-atlas.h" #define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) @@ -61,4 +62,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, CoglTextureFlags flags, CoglPixelFormat internal_format); +CoglAtlas * +_cogl_atlas_texture_get_atlas (void); + #endif /* __COGL_ATLAS_TEXTURE_H */ diff --git a/clutter/cogl/cogl/cogl-atlas-texture.c b/clutter/cogl/cogl/cogl-atlas-texture.c index 4afba0fc5..93ee87c9e 100644 --- a/clutter/cogl/cogl/cogl-atlas-texture.c +++ b/clutter/cogl/cogl/cogl-atlas-texture.c @@ -42,172 +42,93 @@ #include "cogl-rectangle-map.h" #include "cogl-journal-private.h" #include "cogl-material-opengl-private.h" +#include "cogl-atlas.h" #include -#ifdef HAVE_COGL_GLES2 - -#include "../gles/cogl-gles2-wrapper.h" - -#else /* HAVE_COGL_GLES2 */ - -#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers -#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer -#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D -#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus -#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers - -#endif /* HAVE_COGL_GLES2 */ - -#ifndef GL_FRAMEBUFFER -#define GL_FRAMEBUFFER 0x8D40 -#endif -#ifndef GL_FRAMEBUFFER_BINDING -#define GL_FRAMEBUFFER_BINDING 0x8CA6 -#endif -#ifndef GL_COLOR_ATTACHMENT0 -#define GL_COLOR_ATTACHMENT0 0x8CE0 -#endif -#ifndef GL_FRAMEBUFFER_COMPLETE -#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#endif - static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); COGL_TEXTURE_INTERNAL_DEFINE (AtlasTexture, atlas_texture); static const CoglTextureVtable cogl_atlas_texture_vtable; -/* If we want to do mulitple blits from a texture (such as when - reorganizing the atlas) then it's quicker to download all of the - data once and upload multiple times from that. This struct is used - to keep the image data for a series of blits */ - -typedef struct _CoglAtlasTextureBlitData +static CoglHandle +_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, + const CoglRectangleMapEntry *rectangle) { - CoglHandle src_tex, dst_tex; - - /* If we're using an FBO to blit, then FBO will be non-zero and - old_fbo will be the previous framebuffer binding */ - GLuint fbo, old_fbo; - - /* If we're not using an FBO then we g_malloc a buffer and copy the - complete texture data in */ - unsigned char *image_data; - CoglPixelFormat format; - int bpp; - unsigned int src_height, src_width; - - GLenum dst_gl_target; -} CoglAtlasTextureBlitData; - -static void -_cogl_atlas_texture_blit_begin (CoglAtlasTextureBlitData *data, - CoglHandle dst_tex, - CoglHandle src_tex) -{ - GLenum src_gl_target; - GLuint src_gl_texture; - GLuint dst_gl_texture; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - data->dst_tex = dst_tex; - data->src_tex = src_tex; - data->fbo = 0; - - /* If we can use an FBO then we don't need to download the data and - we can tell GL to blit directly between the textures */ - if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && - !cogl_texture_is_sliced (dst_tex) && - cogl_texture_get_gl_texture (src_tex, &src_gl_texture, &src_gl_target) && - cogl_texture_get_gl_texture (dst_tex, &dst_gl_texture, - &data->dst_gl_target)) - { - /* Ideally we would use the cogl-offscreen API here, but I'd - rather avoid creating a stencil renderbuffer which you can't - currently do */ - /* Preserve the previous framebuffer binding so we don't trample - on cogl-offscreen */ - data->old_fbo = 0; - GE( glGetIntegerv (GL_FRAMEBUFFER_BINDING, (GLint *) &data->old_fbo) ); - - _cogl_texture_set_filters (src_tex, GL_NEAREST, GL_NEAREST); - - /* Create an FBO to read from the src texture */ - GE( glGenFramebuffers (1, &data->fbo) ); - GE( glBindFramebuffer (GL_FRAMEBUFFER, data->fbo) ); - GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - src_gl_target, src_gl_texture, 0) ); - if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - /* The FBO failed for whatever reason so we'll fallback to - reading the texture data */ - GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); - GE( glDeleteFramebuffers (1, &data->fbo) ); - data->fbo = 0; - } - - _cogl_bind_gl_texture_transient (data->dst_gl_target, - dst_gl_texture, - FALSE); - } - - if (data->fbo) - COGL_NOTE (ATLAS, "Blit set up using an FBO"); - else - { - /* We need to retrieve the entire texture data (there is no - glGetTexSubImage2D) */ - - data->format = cogl_texture_get_format (src_tex); - data->bpp = _cogl_get_format_bpp (data->format); - data->src_width = cogl_texture_get_width (src_tex); - data->src_height = cogl_texture_get_height (src_tex); - - data->image_data = g_malloc (data->bpp * data->src_width * - data->src_height); - cogl_texture_get_data (src_tex, data->format, - data->src_width * data->bpp, data->image_data); - } + /* Create a subtexture for the given rectangle not including the + 1-pixel border */ + return _cogl_sub_texture_new (full_texture, + rectangle->x + 1, + rectangle->y + 1, + rectangle->width - 2, + rectangle->height - 2); } static void -_cogl_atlas_texture_blit (CoglAtlasTextureBlitData *data, - unsigned int src_x, - unsigned int src_y, - unsigned int dst_x, - unsigned int dst_y, - unsigned int width, - unsigned int height) +_cogl_atlas_texture_update_position_cb (gpointer user_data, + CoglHandle new_texture, + const CoglRectangleMapEntry *rectangle) { - /* If we have an FBO then we can do a fast blit */ - if (data->fbo) - GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y, - width, height) ); - else - cogl_texture_set_region (data->dst_tex, - src_x, src_y, - dst_x, dst_y, - width, height, - data->src_width, data->src_height, - data->format, - data->src_width * data->bpp, - data->image_data); + CoglAtlasTexture *atlas_tex = user_data; + + /* Update the sub texture */ + if (atlas_tex->sub_texture) + cogl_handle_unref (atlas_tex->sub_texture); + atlas_tex->sub_texture = + _cogl_atlas_texture_create_sub_texture (new_texture, rectangle); + + /* Update the position */ + atlas_tex->rectangle = *rectangle; } static void -_cogl_atlas_texture_blit_end (CoglAtlasTextureBlitData *data) +_cogl_atlas_texture_reorganize_foreach_cb (const CoglRectangleMapEntry *entry, + void *rectangle_data, + void *user_data) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Notify cogl-material.c that the texture's underlying GL texture + * storage is changing so it knows it may need to bind a new texture + * if the CoglTexture is reused with the same texture unit. */ + _cogl_material_texture_storage_change_notify (rectangle_data); +} - if (data->fbo) +static void +_cogl_atlas_texture_reorganize_cb (void *data) +{ + CoglAtlas *atlas; + /* We don't know if any materials may currently be referenced in + * the journal that depend on the current underlying GL texture + * storage so we flush the journal before migrating. + * + * We are assuming that texture atlas migration never happens + * during a flush so we don't have to consider recursion here. + */ + _cogl_journal_flush (); + + atlas = _cogl_atlas_texture_get_atlas (); + + if (atlas->map) + _cogl_rectangle_map_foreach (atlas->map, + _cogl_atlas_texture_reorganize_foreach_cb, + NULL); +} + +CoglAtlas * +_cogl_atlas_texture_get_atlas (void) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + if (ctx->atlas == COGL_INVALID_HANDLE) { - GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); - GE( glDeleteFramebuffers (1, &data->fbo) ); + ctx->atlas = _cogl_atlas_new (_cogl_atlas_texture_update_position_cb); + + _cogl_atlas_add_reorganize_callback (ctx->atlas, + _cogl_atlas_texture_reorganize_cb, + NULL); } - else - g_free (data->image_data); + + return ctx->atlas; } static void @@ -252,20 +173,8 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { if (atlas_tex->in_atlas) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_rectangle_map_remove (ctx->rectangle_map, &atlas_tex->rectangle); - - COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", - atlas_tex->rectangle.width, - atlas_tex->rectangle.height); - COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - _cogl_rectangle_map_get_width (ctx->rectangle_map), - _cogl_rectangle_map_get_height (ctx->rectangle_map), - _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map), - _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) * - 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) * - _cogl_rectangle_map_get_height (ctx->rectangle_map))); + _cogl_atlas_remove (_cogl_atlas_texture_get_atlas (), + &atlas_tex->rectangle); atlas_tex->in_atlas = FALSE; } @@ -361,10 +270,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) /* Make sure this texture is not in the atlas */ if (atlas_tex->in_atlas) { - CoglAtlasTextureBlitData blit_data; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); /* We don't know if any materials may currently be referenced in @@ -383,27 +288,15 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) cogl_handle_unref (atlas_tex->sub_texture); - /* Create a new texture at the right size, not including the - border */ atlas_tex->sub_texture = - cogl_texture_new_with_size (atlas_tex->rectangle.width - 2, + _cogl_atlas_copy_rectangle (_cogl_atlas_texture_get_atlas (), + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + 1, + atlas_tex->rectangle.width - 2, atlas_tex->rectangle.height - 2, COGL_TEXTURE_NO_ATLAS, atlas_tex->format); - /* 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 - atlas texture */ - _cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture, - ctx->atlas_texture); - _cogl_atlas_texture_blit (&blit_data, - atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y + 1, - 0, 0, - atlas_tex->rectangle.width - 2, - atlas_tex->rectangle.height - 2); - _cogl_atlas_texture_blit_end (&blit_data); - _cogl_atlas_texture_remove_from_atlas (atlas_tex); } } @@ -445,10 +338,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, unsigned int dst_height, CoglBitmap *bmp) { - _COGL_GET_CONTEXT (ctx, FALSE); + CoglAtlas *atlas = _cogl_atlas_texture_get_atlas (); /* Copy the central data */ - if (!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture, + if (!_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, dst_x + atlas_tex->rectangle.x + 1, dst_y + atlas_tex->rectangle.y + 1, @@ -459,7 +352,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, /* Update the left edge pixels */ if (dst_x == 0 && - !_cogl_texture_set_region_from_bitmap (ctx->atlas_texture, + !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, atlas_tex->rectangle.x, dst_y + atlas_tex->rectangle.y + 1, @@ -468,7 +361,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the right edge pixels */ if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && - !_cogl_texture_set_region_from_bitmap (ctx->atlas_texture, + !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x + dst_width - 1, src_y, atlas_tex->rectangle.x + atlas_tex->rectangle.width - 1, @@ -478,7 +371,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the top edge pixels */ if (dst_y == 0 && - !_cogl_texture_set_region_from_bitmap (ctx->atlas_texture, + !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y, @@ -487,7 +380,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, return FALSE; /* Update the bottom edge pixels */ if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && - !_cogl_texture_set_region_from_bitmap (ctx->atlas_texture, + !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y + dst_height - 1, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + @@ -597,319 +490,6 @@ _cogl_atlas_texture_get_height (CoglTexture *tex) return cogl_texture_get_height (atlas_tex->sub_texture); } -static CoglHandle -_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, - const CoglRectangleMapEntry *rectangle) -{ - /* Create a subtexture for the given rectangle not including the - 1-pixel border */ - return _cogl_sub_texture_new (full_texture, - rectangle->x + 1, - rectangle->y + 1, - rectangle->width - 2, - rectangle->height - 2); -} - -typedef struct _CoglAtlasTextureRepositionData -{ - /* The current texture which already has a position */ - CoglAtlasTexture *texture; - /* The new position of the texture */ - CoglRectangleMapEntry new_position; -} CoglAtlasTextureRepositionData; - -static void -_cogl_atlas_texture_migrate (unsigned int n_textures, - CoglAtlasTextureRepositionData *textures, - CoglHandle old_texture, - CoglHandle new_texture, - CoglAtlasTexture *skip_texture) -{ - unsigned int i; - CoglAtlasTextureBlitData blit_data; - - /* We don't know if any materials may currently be referenced in the - * journal that depend on the current underlying GL texture storage - * so we flush the journal before migrating. - * - * We are assuming that texture atlas migration never happens during - * a flush so we don't have to consider recursion here. - */ - _cogl_journal_flush (); - - _cogl_atlas_texture_blit_begin (&blit_data, new_texture, old_texture); - - for (i = 0; i < n_textures; i++) - { - /* Notify cogl-material.c that the texture's underlying GL texture - * storage is changing so it knows it may need to bind a new texture - * if the CoglTexture is reused with the same texture unit. */ - _cogl_material_texture_storage_change_notify (textures[i].texture); - - /* Skip the texture that is being added because it doesn't contain - any data yet */ - if (textures[i].texture != skip_texture) - { - _cogl_atlas_texture_blit (&blit_data, - textures[i].texture->rectangle.x, - textures[i].texture->rectangle.y, - textures[i].new_position.x, - textures[i].new_position.y, - textures[i].new_position.width, - textures[i].new_position.height); - /* Update the sub texture */ - cogl_handle_unref (textures[i].texture->sub_texture); - textures[i].texture->sub_texture = - _cogl_atlas_texture_create_sub_texture (new_texture, - &textures[i].new_position); - } - - /* Update the texture position */ - textures[i].texture->rectangle = textures[i].new_position; - } - - _cogl_atlas_texture_blit_end (&blit_data); -} - -typedef struct _CoglAtlasTextureGetRectanglesData -{ - CoglAtlasTextureRepositionData *textures; - /* Number of textures found so far */ - unsigned int n_textures; -} CoglAtlasTextureGetRectanglesData; - -static void -_cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *rectangle, - gpointer rect_data, - gpointer user_data) -{ - CoglAtlasTextureGetRectanglesData *data = user_data; - - data->textures[data->n_textures++].texture = rect_data; -} - -static void -_cogl_atlas_texture_get_next_size (unsigned int *map_width, - unsigned int *map_height) -{ - /* Double the size of the texture by increasing whichever dimension - is smaller */ - if (*map_width < *map_height) - *map_width <<= 1; - else - *map_height <<= 1; -} - -static CoglRectangleMap * -_cogl_atlas_texture_create_map (unsigned int map_width, - unsigned int map_height, - unsigned int n_textures, - CoglAtlasTextureRepositionData *textures) -{ - GLint max_texture_size = 1024; - - GE( glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size) ); - - /* Sanity check that we're not going to get stuck in an infinite - loop if the maximum texture size has the high bit set */ - if ((max_texture_size & (1 << (sizeof (GLint) * 8 - 2)))) - max_texture_size >>= 1; - - /* Keep trying increasingly larger atlases until we can fit all of - the textures */ - while (map_width < max_texture_size && map_height < max_texture_size) - { - CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width, - map_height, - NULL); - unsigned int i; - - /* Add all of the textures and keep track of the new position */ - for (i = 0; i < n_textures; i++) - if (!_cogl_rectangle_map_add (new_atlas, - textures[i].texture->rectangle.width, - textures[i].texture->rectangle.height, - textures[i].texture, - &textures[i].new_position)) - break; - - /* If the atlas can contain all of the textures then we have a - winner */ - if (i >= n_textures) - return new_atlas; - - _cogl_rectangle_map_free (new_atlas); - _cogl_atlas_texture_get_next_size (&map_width, &map_height); - } - - /* If we get here then there's no atlas that can accommodate all of - the rectangles */ - - return NULL; -} - -static int -_cogl_atlas_texture_compare_size_cb (const void *a, - const void *b) -{ - const CoglAtlasTextureRepositionData *ta = a; - const CoglAtlasTextureRepositionData *tb = b; - unsigned int a_size, b_size; - - a_size = ta->texture->rectangle.width * ta->texture->rectangle.height; - b_size = tb->texture->rectangle.width * tb->texture->rectangle.height; - - return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; -} - -static gboolean -_cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, - unsigned int width, - unsigned int height) -{ - CoglAtlasTextureGetRectanglesData data; - CoglRectangleMap *new_map; - CoglHandle new_tex; - unsigned int map_width, map_height; - gboolean ret; - - _COGL_GET_CONTEXT (ctx, FALSE); - - /* Check if we can fit the rectangle into the existing atlas */ - if (ctx->rectangle_map && - _cogl_rectangle_map_add (ctx->rectangle_map, width, height, - new_sub_tex, - &new_sub_tex->rectangle)) - { - COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - _cogl_rectangle_map_get_width (ctx->rectangle_map), - _cogl_rectangle_map_get_height (ctx->rectangle_map), - _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map), - _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) * - 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) * - _cogl_rectangle_map_get_height (ctx->rectangle_map))); - - return TRUE; - } - - /* We need to reorganise the atlas so we'll get an array of all the - textures currently in the atlas. */ - data.n_textures = 0; - if (ctx->rectangle_map == NULL) - data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); - else - { - unsigned int n_rectangles = - _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map); - data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) * - (n_rectangles + 1)); - _cogl_rectangle_map_foreach (ctx->rectangle_map, - _cogl_atlas_texture_get_rectangles_cb, - &data); - } - - /* Add the new rectangle as a dummy texture so that it can be - positioned with the rest */ - data.textures[data.n_textures++].texture = new_sub_tex; - - /* The atlasing algorithm works a lot better if the rectangles are - added in decreasing order of size so we'll first sort the - array */ - qsort (data.textures, data.n_textures, - sizeof (CoglAtlasTextureRepositionData), - _cogl_atlas_texture_compare_size_cb); - - /* Try to create a new atlas that can contain all of the textures */ - if (ctx->rectangle_map) - { - map_width = _cogl_rectangle_map_get_width (ctx->rectangle_map); - map_height = _cogl_rectangle_map_get_height (ctx->rectangle_map); - - /* If there is enough space in the existing for the new - rectangle in the existing atlas we'll start with the same - size, otherwise we'll immediately double it */ - if (_cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) < - width * height) - _cogl_atlas_texture_get_next_size (&map_width, &map_height); - } - else - { - /* Start with an initial size of 256x256 */ - map_width = 256; - map_height = 256; - } - - new_map = _cogl_atlas_texture_create_map (map_width, map_height, - data.n_textures, data.textures); - map_width = _cogl_rectangle_map_get_width (new_map); - map_height = _cogl_rectangle_map_get_height (new_map); - - /* If we can't create a map with the texture then give up */ - if (new_map == NULL) - { - COGL_NOTE (ATLAS, "Could not fit texture in the atlas"); - ret = FALSE; - } - /* We need to migrate the existing textures into a new texture */ - else if ((new_tex = - _cogl_texture_2d_new_with_size (map_width, map_height, - COGL_TEXTURE_NONE, - COGL_PIXEL_FORMAT_RGBA_8888)) == - COGL_INVALID_HANDLE) - { - COGL_NOTE (ATLAS, "Could not create a CoglTexture2D"); - _cogl_rectangle_map_free (new_map); - ret = FALSE; - } - else - { - COGL_NOTE (ATLAS, - "Atlas %s with size %ix%i", - ctx->rectangle_map == NULL || - _cogl_rectangle_map_get_width (ctx->rectangle_map) != - _cogl_rectangle_map_get_width (new_map) || - _cogl_rectangle_map_get_height (ctx->rectangle_map) != - _cogl_rectangle_map_get_height (new_map) ? - "resized" : "reorganized", - _cogl_rectangle_map_get_width (new_map), - _cogl_rectangle_map_get_height (new_map)); - - if (ctx->rectangle_map) - { - /* Move all the textures to the right position in the new - texture. This will also update the texture's rectangle */ - _cogl_atlas_texture_migrate (data.n_textures, - data.textures, - ctx->atlas_texture, - new_tex, - new_sub_tex); - _cogl_rectangle_map_free (ctx->rectangle_map); - cogl_handle_unref (ctx->atlas_texture); - } - else - /* We know there's only one texture so we can just directly - update the rectangle from its new position */ - data.textures[0].texture->rectangle = data.textures[0].new_position; - - ctx->rectangle_map = new_map; - ctx->atlas_texture = new_tex; - - COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", - _cogl_rectangle_map_get_width (ctx->rectangle_map), - _cogl_rectangle_map_get_height (ctx->rectangle_map), - _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map), - _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) * - 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) * - _cogl_rectangle_map_get_height (ctx->rectangle_map))); - - ret = TRUE; - } - - g_free (data.textures); - - return ret; -} - static gboolean _cogl_atlas_texture_can_use_format (CoglPixelFormat format) { @@ -987,15 +567,14 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, /* We need to allocate the texture now because we need the pointer to set as the data for the rectangle in the atlas */ atlas_tex = g_new (CoglAtlasTexture, 1); - /* We need to fill in the texture size now because it is used in the - reserve_space function below. We add two pixels for the border */ - atlas_tex->rectangle.width = bmp_width + 2; - atlas_tex->rectangle.height = bmp_height + 2; + + atlas_tex->sub_texture = COGL_INVALID_HANDLE; /* Try to make some space in the atlas for the texture */ - if (!_cogl_atlas_texture_reserve_space (atlas_tex, - atlas_tex->rectangle.width, - atlas_tex->rectangle.height)) + if (!_cogl_atlas_reserve_space (_cogl_atlas_texture_get_atlas (), + /* Add two pixels for the border */ + bmp_width + 2, bmp_height + 2, + atlas_tex)) { g_free (atlas_tex); return COGL_INVALID_HANDLE; @@ -1010,7 +589,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, if (dst_bmp == NULL) { - _cogl_rectangle_map_remove (ctx->rectangle_map, &atlas_tex->rectangle); + _cogl_atlas_remove (_cogl_atlas_texture_get_atlas (), + &atlas_tex->rectangle); g_free (atlas_tex); return COGL_INVALID_HANDLE; } @@ -1018,9 +598,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; atlas_tex->format = internal_format; atlas_tex->in_atlas = TRUE; - atlas_tex->sub_texture = - _cogl_atlas_texture_create_sub_texture (ctx->atlas_texture, - &atlas_tex->rectangle); /* Make another bitmap so that we can override the format */ override_bmp = _cogl_bitmap_new_shared (dst_bmp, diff --git a/clutter/cogl/cogl/cogl-atlas.c b/clutter/cogl/cogl/cogl-atlas.c new file mode 100644 index 000000000..8e3313088 --- /dev/null +++ b/clutter/cogl/cogl/cogl-atlas.c @@ -0,0 +1,596 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-atlas.h" +#include "cogl-rectangle-map.h" +#include "cogl-context.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-material-opengl-private.h" +#include "cogl-debug.h" + +#include + +#ifdef HAVE_COGL_GLES2 + +#include "../gles/cogl-gles2-wrapper.h" + +#else /* HAVE_COGL_GLES2 */ + +#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers +#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer +#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D +#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus +#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers + +#endif /* HAVE_COGL_GLES2 */ + +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif + +/* If we want to do mulitple blits from a texture (such as when + reorganizing the atlas) then it's quicker to download all of the + data once and upload multiple times from that. This struct is used + to keep the image data for a series of blits */ + +typedef struct _CoglAtlasBlitData +{ + CoglHandle src_tex, dst_tex; + + /* If we're using an FBO to blit, then FBO will be non-zero and + old_fbo will be the previous framebuffer binding */ + GLuint fbo, old_fbo; + + /* If we're not using an FBO then we g_malloc a buffer and copy the + complete texture data in */ + unsigned char *image_data; + CoglPixelFormat format; + int bpp; + unsigned int src_height, src_width; + + GLenum dst_gl_target; +} CoglAtlasBlitData; + +static void +_cogl_atlas_blit_begin (CoglAtlasBlitData *data, + CoglHandle dst_tex, + CoglHandle src_tex) +{ + GLenum src_gl_target; + GLuint src_gl_texture; + GLuint dst_gl_texture; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + data->dst_tex = dst_tex; + data->src_tex = src_tex; + data->fbo = 0; + + /* If we can use an FBO then we don't need to download the data and + we can tell GL to blit directly between the textures */ + if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && + !cogl_texture_is_sliced (dst_tex) && + cogl_texture_get_gl_texture (src_tex, &src_gl_texture, &src_gl_target) && + cogl_texture_get_gl_texture (dst_tex, &dst_gl_texture, + &data->dst_gl_target)) + { + /* Ideally we would use the cogl-offscreen API here, but I'd + rather avoid creating a stencil renderbuffer which you can't + currently do */ + /* Preserve the previous framebuffer binding so we don't trample + on cogl-offscreen */ + data->old_fbo = 0; + GE( glGetIntegerv (GL_FRAMEBUFFER_BINDING, (GLint *) &data->old_fbo) ); + + _cogl_texture_set_filters (src_tex, GL_NEAREST, GL_NEAREST); + + /* Create an FBO to read from the src texture */ + GE( glGenFramebuffers (1, &data->fbo) ); + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->fbo) ); + GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + src_gl_target, src_gl_texture, 0) ); + if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + /* The FBO failed for whatever reason so we'll fallback to + reading the texture data */ + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + data->fbo = 0; + } + + _cogl_bind_gl_texture_transient (data->dst_gl_target, + dst_gl_texture, + FALSE); + } + + if (data->fbo) + COGL_NOTE (ATLAS, "Blit set up using an FBO"); + else + { + /* We need to retrieve the entire texture data (there is no + glGetTexSubImage2D) */ + + data->format = cogl_texture_get_format (src_tex); + data->bpp = _cogl_get_format_bpp (data->format); + data->src_width = cogl_texture_get_width (src_tex); + data->src_height = cogl_texture_get_height (src_tex); + + data->image_data = g_malloc (data->bpp * data->src_width * + data->src_height); + cogl_texture_get_data (src_tex, data->format, + data->src_width * data->bpp, data->image_data); + } +} + +static void +_cogl_atlas_blit (CoglAtlasBlitData *data, + unsigned int src_x, + unsigned int src_y, + unsigned int dst_x, + unsigned int dst_y, + unsigned int width, + unsigned int height) +{ + /* If we have an FBO then we can do a fast blit */ + if (data->fbo) + GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y, + width, height) ); + else + cogl_texture_set_region (data->dst_tex, + src_x, src_y, + dst_x, dst_y, + width, height, + data->src_width, data->src_height, + data->format, + data->src_width * data->bpp, + data->image_data); +} + +static void +_cogl_atlas_blit_end (CoglAtlasBlitData *data) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (data->fbo) + { + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + } + else + g_free (data->image_data); +} + +CoglAtlas * +_cogl_atlas_new (CoglAtlasUpdatePositionCallback update_position_cb) +{ + CoglAtlas *atlas = g_new (CoglAtlas, 1); + + atlas->update_position_cb = update_position_cb; + atlas->map = NULL; + atlas->texture = NULL; + _cogl_callback_list_init (&atlas->reorganize_callbacks); + + return atlas; +} + +void +_cogl_atlas_free (CoglAtlas *atlas) +{ + if (atlas->texture) + cogl_handle_unref (atlas->texture); + if (atlas->map) + _cogl_rectangle_map_free (atlas->map); + + _cogl_callback_list_destroy (&atlas->reorganize_callbacks); + + g_free (atlas); +} + +typedef struct _CoglAtlasRepositionData +{ + /* The current user data for this texture */ + void *user_data; + /* The old and new positions of the texture */ + CoglRectangleMapEntry old_position; + CoglRectangleMapEntry new_position; +} CoglAtlasRepositionData; + +static void +_cogl_atlas_migrate (CoglAtlas *atlas, + unsigned int n_textures, + CoglAtlasRepositionData *textures, + CoglHandle old_texture, + CoglHandle new_texture, + void *skip_user_data) +{ + unsigned int i; + CoglAtlasBlitData blit_data; + + _cogl_atlas_blit_begin (&blit_data, new_texture, old_texture); + + for (i = 0; i < n_textures; i++) + { + /* Skip the texture that is being added because it doesn't contain + any data yet */ + if (textures[i].user_data != skip_user_data) + { + _cogl_atlas_blit (&blit_data, + textures[i].old_position.x, + textures[i].old_position.y, + textures[i].new_position.x, + textures[i].new_position.y, + textures[i].new_position.width, + textures[i].new_position.height); + } + /* Update the texture position */ + atlas->update_position_cb (textures[i].user_data, + new_texture, + &textures[i].new_position); + } + + _cogl_atlas_blit_end (&blit_data); +} + +typedef struct _CoglAtlasGetRectanglesData +{ + CoglAtlasRepositionData *textures; + /* Number of textures found so far */ + unsigned int n_textures; +} CoglAtlasGetRectanglesData; + +static void +_cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle, + void *rect_data, + void *user_data) +{ + CoglAtlasGetRectanglesData *data = user_data; + + data->textures[data->n_textures].old_position = *rectangle; + data->textures[data->n_textures++].user_data = rect_data; +} + +static void +_cogl_atlas_get_next_size (unsigned int *map_width, + unsigned int *map_height) +{ + /* Double the size of the texture by increasing whichever dimension + is smaller */ + if (*map_width < *map_height) + *map_width <<= 1; + else + *map_height <<= 1; +} + +static CoglRectangleMap * +_cogl_atlas_create_map (unsigned int map_width, + unsigned int map_height, + unsigned int n_textures, + CoglAtlasRepositionData *textures) +{ + GLint max_texture_size = 1024; + + GE( glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size) ); + + /* Sanity check that we're not going to get stuck in an infinite + loop if the maximum texture size has the high bit set */ + if ((max_texture_size & (1 << (sizeof (GLint) * 8 - 2)))) + max_texture_size >>= 1; + + /* Keep trying increasingly larger atlases until we can fit all of + the textures */ + while (map_width < max_texture_size && map_height < max_texture_size) + { + CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width, + map_height, + NULL); + unsigned int i; + + /* Add all of the textures and keep track of the new position */ + for (i = 0; i < n_textures; i++) + if (!_cogl_rectangle_map_add (new_atlas, + textures[i].old_position.width, + textures[i].old_position.height, + textures[i].user_data, + &textures[i].new_position)) + break; + + /* If the atlas can contain all of the textures then we have a + winner */ + if (i >= n_textures) + return new_atlas; + + _cogl_rectangle_map_free (new_atlas); + _cogl_atlas_get_next_size (&map_width, &map_height); + } + + /* If we get here then there's no atlas that can accommodate all of + the rectangles */ + + return NULL; +} + +static int +_cogl_atlas_compare_size_cb (const void *a, + const void *b) +{ + const CoglAtlasRepositionData *ta = a; + const CoglAtlasRepositionData *tb = b; + unsigned int a_size, b_size; + + a_size = ta->old_position.width * ta->old_position.height; + b_size = tb->old_position.width * tb->old_position.height; + + return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; +} + +static void +_cogl_atlas_notify_reorganize (CoglAtlas *atlas) +{ + _cogl_callback_list_invoke (&atlas->reorganize_callbacks); +} + +gboolean +_cogl_atlas_reserve_space (CoglAtlas *atlas, + unsigned int width, + unsigned int height, + void *user_data) +{ + CoglAtlasGetRectanglesData data; + CoglRectangleMap *new_map; + CoglHandle new_tex; + unsigned int map_width, map_height; + gboolean ret; + CoglRectangleMapEntry new_position; + + /* Check if we can fit the rectangle into the existing map */ + if (atlas->map && + _cogl_rectangle_map_add (atlas->map, width, height, + user_data, + &new_position)) + { + int waste = + _cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map)); + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + waste); + + atlas->update_position_cb (user_data, + atlas->texture, + &new_position); + + return TRUE; + } + + /* We need to reorganise the atlas so we'll get an array of all the + textures currently in the atlas. */ + data.n_textures = 0; + if (atlas->map == NULL) + data.textures = g_malloc (sizeof (CoglAtlasRepositionData)); + else + { + unsigned int n_rectangles = + _cogl_rectangle_map_get_n_rectangles (atlas->map); + data.textures = g_malloc (sizeof (CoglAtlasRepositionData) * + (n_rectangles + 1)); + _cogl_rectangle_map_foreach (atlas->map, + _cogl_atlas_get_rectangles_cb, + &data); + } + + /* Add the new rectangle as a dummy texture so that it can be + positioned with the rest */ + data.textures[data.n_textures].old_position.x = 0; + data.textures[data.n_textures].old_position.y = 0; + data.textures[data.n_textures].old_position.width = width; + data.textures[data.n_textures].old_position.height = height; + data.textures[data.n_textures++].user_data = user_data; + + /* The atlasing algorithm works a lot better if the rectangles are + added in decreasing order of size so we'll first sort the + array */ + qsort (data.textures, data.n_textures, + sizeof (CoglAtlasRepositionData), + _cogl_atlas_compare_size_cb); + + /* Try to create a new atlas that can contain all of the textures */ + if (atlas->map) + { + map_width = _cogl_rectangle_map_get_width (atlas->map); + map_height = _cogl_rectangle_map_get_height (atlas->map); + + /* If there is enough space in the existing for the new + rectangle in the existing atlas we'll start with the same + size, otherwise we'll immediately double it */ + if (_cogl_rectangle_map_get_remaining_space (atlas->map) < + width * height) + _cogl_atlas_get_next_size (&map_width, &map_height); + } + else + { + /* Start with an initial size of 256x256 */ + map_width = 256; + map_height = 256; + } + + new_map = _cogl_atlas_create_map (map_width, map_height, + data.n_textures, data.textures); + + /* If we can't create a map with the texture then give up */ + if (new_map == NULL) + { + COGL_NOTE (ATLAS, "Could not fit texture in the atlas"); + ret = FALSE; + } + /* We need to migrate the existing textures into a new texture */ + else if ((new_tex = _cogl_texture_2d_new_with_size + (_cogl_rectangle_map_get_width (new_map), + _cogl_rectangle_map_get_height (new_map), + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888)) == COGL_INVALID_HANDLE) + { + COGL_NOTE (ATLAS, "Could not create a CoglTexture2D"); + _cogl_rectangle_map_free (new_map); + ret = FALSE; + } + else + { + int waste; + + _cogl_atlas_notify_reorganize (atlas); + + COGL_NOTE (ATLAS, + "Atlas %s with size %ix%i", + atlas->map == NULL || + _cogl_rectangle_map_get_width (atlas->map) != + _cogl_rectangle_map_get_width (new_map) || + _cogl_rectangle_map_get_height (atlas->map) != + _cogl_rectangle_map_get_height (new_map) ? + "resized" : "reorganized", + _cogl_rectangle_map_get_width (new_map), + _cogl_rectangle_map_get_height (new_map)); + + if (atlas->map) + { + /* Move all the textures to the right position in the new + texture. This will also update the texture's rectangle */ + _cogl_atlas_migrate (atlas, + data.n_textures, + data.textures, + atlas->texture, + new_tex, + user_data); + _cogl_rectangle_map_free (atlas->map); + cogl_handle_unref (atlas->texture); + } + else + /* We know there's only one texture so we can just directly + update the rectangle from its new position */ + atlas->update_position_cb (data.textures[0].user_data, + new_tex, + &data.textures[0].new_position); + + atlas->map = new_map; + atlas->texture = new_tex; + + waste = (_cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map))); + + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + waste); + + ret = TRUE; + } + + g_free (data.textures); + + return ret; +} + +void +_cogl_atlas_remove (CoglAtlas *atlas, + const CoglRectangleMapEntry *rectangle) +{ + _cogl_rectangle_map_remove (atlas->map, rectangle); + + COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", + rectangle->width, + rectangle->height); + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_rectangle_map_get_width (atlas->map), + _cogl_rectangle_map_get_height (atlas->map), + _cogl_rectangle_map_get_n_rectangles (atlas->map), + _cogl_rectangle_map_get_remaining_space (atlas->map) * + 100 / (_cogl_rectangle_map_get_width (atlas->map) * + _cogl_rectangle_map_get_height (atlas->map))); +}; + +CoglHandle +_cogl_atlas_copy_rectangle (CoglAtlas *atlas, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat format) +{ + CoglHandle tex; + CoglAtlasBlitData blit_data; + + /* Create a new texture at the right size */ + tex = cogl_texture_new_with_size (width, height, flags, format); + + /* 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 + atlas texture */ + _cogl_atlas_blit_begin (&blit_data, tex, atlas->texture); + _cogl_atlas_blit (&blit_data, + x, y, + 0, 0, + width, height); + _cogl_atlas_blit_end (&blit_data); + + return tex; +} + +void +_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, + CoglCallbackListFunc callback, + void *user_data) +{ + _cogl_callback_list_add (&atlas->reorganize_callbacks, callback, user_data); +} + +void +_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, + CoglCallbackListFunc callback, + void *user_data) +{ + _cogl_callback_list_remove (&atlas->reorganize_callbacks, + callback, user_data); +} diff --git a/clutter/cogl/cogl/cogl-atlas.h b/clutter/cogl/cogl/cogl-atlas.h new file mode 100644 index 000000000..93b7d9fbd --- /dev/null +++ b/clutter/cogl/cogl/cogl-atlas.h @@ -0,0 +1,86 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_ATLAS_H +#define __COGL_ATLAS_H + +#include "cogl-rectangle-map.h" +#include "cogl-callback-list.h" + +typedef void +(* CoglAtlasUpdatePositionCallback) (void *user_data, + CoglHandle new_texture, + const CoglRectangleMapEntry *rect); + +typedef struct _CoglAtlas CoglAtlas; + +struct _CoglAtlas +{ + CoglRectangleMap *map; + + CoglHandle texture; + + CoglAtlasUpdatePositionCallback update_position_cb; + + CoglCallbackList reorganize_callbacks; +}; + +CoglAtlas * +_cogl_atlas_new (CoglAtlasUpdatePositionCallback update_position_cb); + +gboolean +_cogl_atlas_reserve_space (CoglAtlas *atlas, + unsigned int width, + unsigned int height, + void *user_data); + +void +_cogl_atlas_remove (CoglAtlas *atlas, + const CoglRectangleMapEntry *rectangle); + +void +_cogl_atlas_free (CoglAtlas *atlas); + +CoglHandle +_cogl_atlas_copy_rectangle (CoglAtlas *atlas, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat format); + +CoglAtlas * +_cogl_atlas_get_default (void); + +void +_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, + CoglCallbackListFunc callback, + void *user_data); + +void +_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, + CoglCallbackListFunc callback, + void *user_data); + +#endif /* __COGL_ATLAS_H */ diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 2668043c2..936291ed3 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/cogl/cogl-context.c @@ -214,8 +214,7 @@ cogl_create_context (void) _cogl_enable (enable_flags); _cogl_flush_face_winding (); - _context->rectangle_map = NULL; - _context->atlas_texture = COGL_INVALID_HANDLE; + _context->atlas = NULL; /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect unless GL_COORD_REPLACE is enabled for an individual @@ -273,11 +272,8 @@ _cogl_destroy_context (void) if (_context->default_layer_0) cogl_handle_unref (_context->default_layer_0); - if (_context->rectangle_map) - _cogl_rectangle_map_free (_context->rectangle_map); - - if (_context->atlas_texture) - cogl_handle_unref (_context->atlas_texture); + if (_context->atlas) + _cogl_atlas_free (_context->atlas); _cogl_bitmask_destroy (&_context->texcoord_arrays_enabled); _cogl_bitmask_destroy (&_context->temp_bitmask); diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index b89ee057f..54815349a 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -31,9 +31,9 @@ #include "cogl-clip-stack.h" #include "cogl-matrix-stack.h" #include "cogl-material-private.h" -#include "cogl-rectangle-map.h" #include "cogl-buffer-private.h" #include "cogl-bitmask.h" +#include "cogl-atlas.h" typedef struct { @@ -143,8 +143,7 @@ typedef struct CoglMaterial *texture_download_material; - CoglRectangleMap *rectangle_map; - CoglHandle atlas_texture; + CoglAtlas *atlas; /* This debugging variable is used to pick a colour for visually displaying the quad batches. It needs to be global so that it can