diff --git a/clutter/cogl/cogl/cogl-atlas-texture-private.h b/clutter/cogl/cogl/cogl-atlas-texture-private.h index eaa6a84e3..cdac7d389 100644 --- a/clutter/cogl/cogl/cogl-atlas-texture-private.h +++ b/clutter/cogl/cogl/cogl-atlas-texture-private.h @@ -46,9 +46,10 @@ struct _CoglAtlasTexture atlas. This includes the 1-pixel border */ CoglRectangleMapEntry rectangle; - /* The texture might need to be migrated out in which case this will - be set to TRUE and sub_texture will actually be a real texture */ - gboolean in_atlas; + /* The atlas that this texture is in. If the texture is no longer in + an atlas then this will be NULL. A reference is taken on the + atlas by the texture (but not vice versa so there is no cycle) */ + CoglAtlas *atlas; /* A CoglSubTexture representing the region for easy rendering */ CoglHandle sub_texture; @@ -62,7 +63,4 @@ _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 9194da3e4..e9b4e6b12 100644 --- a/clutter/cogl/cogl/cogl-atlas-texture.c +++ b/clutter/cogl/cogl/cogl-atlas-texture.c @@ -96,7 +96,8 @@ _cogl_atlas_texture_reorganize_foreach_cb (const CoglRectangleMapEntry *entry, static void _cogl_atlas_texture_reorganize_cb (void *data) { - CoglAtlas *atlas; + CoglAtlas *atlas = data; + /* We don't know if any pipelines may currently be referenced in * the journal that depend on the current underlying GL texture * storage so we flush the journal before migrating. @@ -106,31 +107,49 @@ _cogl_atlas_texture_reorganize_cb (void *data) */ _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) +static void +_cogl_atlas_texture_atlas_destroyed_cb (void *user_data) { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Remove the atlas from the global list */ + ctx->atlases = g_slist_remove (ctx->atlases, user_data); +} + +static CoglAtlas * +_cogl_atlas_texture_create_atlas (void) +{ + static CoglUserDataKey atlas_private_key; + + CoglAtlas *atlas; + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); - if (ctx->atlas == COGL_INVALID_HANDLE) - { - ctx->atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888, - 0, - _cogl_atlas_texture_update_position_cb); + atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888, + 0, + _cogl_atlas_texture_update_position_cb); - _cogl_atlas_add_reorganize_callback (ctx->atlas, - _cogl_atlas_texture_reorganize_cb, - NULL); - } + _cogl_atlas_add_reorganize_callback (atlas, + _cogl_atlas_texture_reorganize_cb, + atlas); - return ctx->atlas; + ctx->atlases = g_slist_prepend (ctx->atlases, atlas); + + /* Set some data on the atlas so we can get notification when it is + destroyed in order to remove it from the list. ctx->atlases + effectively holds a weak reference. We don't need a strong + reference because the atlas textures take a reference on the + atlas so it will stay alive */ + cogl_object_set_user_data (COGL_OBJECT (atlas), &atlas_private_key, atlas, + _cogl_atlas_texture_atlas_destroyed_cb); + + return atlas; } static void @@ -173,12 +192,13 @@ _cogl_atlas_texture_set_wrap_mode_parameters (CoglTexture *tex, static void _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { - if (atlas_tex->in_atlas) + if (atlas_tex->atlas) { - _cogl_atlas_remove (_cogl_atlas_texture_get_atlas (), + _cogl_atlas_remove (atlas_tex->atlas, &atlas_tex->rectangle); - atlas_tex->in_atlas = FALSE; + cogl_object_unref (atlas_tex->atlas); + atlas_tex->atlas = NULL; } } @@ -270,7 +290,7 @@ static void _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) { /* Make sure this texture is not in the atlas */ - if (atlas_tex->in_atlas) + if (atlas_tex->atlas) { COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); @@ -291,7 +311,7 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) cogl_handle_unref (atlas_tex->sub_texture); atlas_tex->sub_texture = - _cogl_atlas_copy_rectangle (_cogl_atlas_texture_get_atlas (), + _cogl_atlas_copy_rectangle (atlas_tex->atlas, atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + 1, atlas_tex->rectangle.width - 2, @@ -340,7 +360,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, unsigned int dst_height, CoglBitmap *bmp) { - CoglAtlas *atlas = _cogl_atlas_texture_get_atlas (); + CoglAtlas *atlas = atlas_tex->atlas; /* Copy the central data */ if (!_cogl_texture_set_region_from_bitmap (atlas->texture, @@ -408,7 +428,7 @@ _cogl_atlas_texture_set_region (CoglTexture *tex, /* If the texture is in the atlas then we need to copy the edge pixels to the border */ - if (atlas_tex->in_atlas) + if (atlas_tex->atlas) { gboolean ret; @@ -505,6 +525,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, int bmp_width; int bmp_height; CoglPixelFormat bmp_format; + CoglAtlas *atlas; + GSList *l; _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); @@ -557,14 +579,33 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, atlas_tex->sub_texture = COGL_INVALID_HANDLE; - /* Try to make some space in the atlas for the texture */ - if (!_cogl_atlas_reserve_space (_cogl_atlas_texture_get_atlas (), - /* Add two pixels for the border */ - bmp_width + 2, bmp_height + 2, - atlas_tex)) + /* 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 */ + bmp_width + 2, bmp_height + 2, + atlas_tex)) + { + cogl_object_ref (atlas); + break; + } + + /* If we couldn't find a suitable atlas then start another */ + if (l == NULL) { - g_free (atlas_tex); - return COGL_INVALID_HANDLE; + atlas = _cogl_atlas_texture_create_atlas (); + COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas); + if (!_cogl_atlas_reserve_space (atlas, + /* Add two pixels for the border */ + bmp_width + 2, bmp_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 COGL_INVALID_HANDLE; + } } dst_bmp = _cogl_texture_prepare_for_upload (bmp, @@ -576,15 +617,15 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, if (dst_bmp == NULL) { - _cogl_atlas_remove (_cogl_atlas_texture_get_atlas (), - &atlas_tex->rectangle); + _cogl_atlas_remove (atlas, &atlas_tex->rectangle); + cogl_object_unref (atlas); g_free (atlas_tex); return COGL_INVALID_HANDLE; } atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; atlas_tex->format = internal_format; - atlas_tex->in_atlas = TRUE; + atlas_tex->atlas = atlas; /* 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 index 4a10db136..2b3a743a8 100644 --- a/clutter/cogl/cogl/cogl-atlas.c +++ b/clutter/cogl/cogl/cogl-atlas.c @@ -489,7 +489,8 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, _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_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), @@ -559,7 +560,7 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, /* 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"); + COGL_NOTE (ATLAS, "%p: Could not fit texture in the atlas", atlas); ret = FALSE; } /* We need to migrate the existing textures into a new texture */ @@ -568,7 +569,7 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, _cogl_rectangle_map_get_width (new_map), _cogl_rectangle_map_get_height (new_map))) == COGL_INVALID_HANDLE) { - COGL_NOTE (ATLAS, "Could not create a CoglTexture2D"); + COGL_NOTE (ATLAS, "%p: Could not create a CoglTexture2D", atlas); _cogl_rectangle_map_free (new_map); ret = FALSE; } @@ -579,7 +580,8 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, _cogl_atlas_notify_reorganize (atlas); COGL_NOTE (ATLAS, - "Atlas %s with size %ix%i", + "%p: Atlas %s with size %ix%i", + atlas, atlas->map == NULL || _cogl_rectangle_map_get_width (atlas->map) != _cogl_rectangle_map_get_width (new_map) || @@ -616,7 +618,8 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, 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_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), @@ -636,10 +639,12 @@ _cogl_atlas_remove (CoglAtlas *atlas, { _cogl_rectangle_map_remove (atlas->map, rectangle); - COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", + COGL_NOTE (ATLAS, "%p: Removed rectangle sized %ix%i", + atlas, rectangle->width, rectangle->height); - COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", + atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), diff --git a/clutter/cogl/cogl/cogl-atlas.h b/clutter/cogl/cogl/cogl-atlas.h index dbe7eb9f7..1c5ae30cf 100644 --- a/clutter/cogl/cogl/cogl-atlas.h +++ b/clutter/cogl/cogl/cogl-atlas.h @@ -82,9 +82,6 @@ _cogl_atlas_copy_rectangle (CoglAtlas *atlas, CoglTextureFlags flags, CoglPixelFormat format); -CoglAtlas * -_cogl_atlas_get_default (void); - void _cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, CoglCallbackListFunc callback, diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index f94f8b713..c8fb1d75e 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/cogl/cogl-context.c @@ -285,7 +285,7 @@ cogl_create_context (void) _cogl_enable (enable_flags); _cogl_flush_face_winding (); - _context->atlas = NULL; + _context->atlases = NULL; /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect unless GL_COORD_REPLACE is enabled for an individual @@ -362,8 +362,7 @@ _cogl_destroy_context (void) if (_context->current_clip_stack_valid) _cogl_clip_stack_unref (_context->current_clip_stack); - if (_context->atlas) - cogl_object_unref (_context->atlas); + g_slist_free (_context->atlases); _cogl_bitmask_destroy (&_context->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 38be97197..1d55923df 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -178,7 +178,7 @@ typedef struct CoglPipeline *texture_download_pipeline; - CoglAtlas *atlas; + GSList *atlases; /* This debugging variable is used to pick a colour for visually displaying the quad batches. It needs to be global so that it can diff --git a/clutter/cogl/pango/cogl-pango-glyph-cache.c b/clutter/cogl/pango/cogl-pango-glyph-cache.c index 1851b237c..c7f09bdef 100644 --- a/clutter/cogl/pango/cogl-pango-glyph-cache.c +++ b/clutter/cogl/pango/cogl-pango-glyph-cache.c @@ -228,6 +228,7 @@ cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8, TRUE, cogl_pango_glyph_cache_update_position_cb); + COGL_NOTE (ATLAS, "Created new atlas for glyphs: %p", atlas); /* If we still can't reserve space then something has gone seriously wrong so we'll just give up */ if (!_cogl_atlas_reserve_space (atlas,