diff options
author | Robert Bragg <robert@linux.intel.com> | 2013-05-08 01:42:24 +0100 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2014-03-11 15:54:42 +0000 |
commit | cbf449fff7b9b24132957940a4072326cff4a7ff (patch) | |
tree | 0695b7e41a95c207fbefbd13a998581e36fe5e9b | |
parent | a041974ef4234231557e5b6bd78ffe612edbb4f7 (diff) | |
download | cogl-wip/public-atlas-apis.tar.gz |
Adds CoglAtlasSet and CoglAtlas apiswip/public-atlas-apis
This enables applications to more closely manage their own texture atlases
via a CoglAtlasSet api. A CoglAtlasSet represents a set of CoglAtlases
and a CoglAtlas represents one large texture that is subdivided into
smaller "allocations".
The intention is to enable functionality like cogl-pango to be
implemented outside of Cogl; specifically to allow Rig to handle its own
glyph caches for text rendering.
-rw-r--r-- | cogl-pango/cogl-pango-glyph-cache.c | 272 | ||||
-rw-r--r-- | cogl-pango/cogl-pango-glyph-cache.h | 2 | ||||
-rw-r--r-- | cogl/Makefile.am | 2 | ||||
-rw-r--r-- | cogl/Makefile.sources | 10 | ||||
-rw-r--r-- | cogl/cogl-atlas-private.h | 88 | ||||
-rw-r--r-- | cogl/cogl-atlas-set-private.h | 58 | ||||
-rw-r--r-- | cogl/cogl-atlas-set.c | 269 | ||||
-rw-r--r-- | cogl/cogl-atlas-set.h | 161 | ||||
-rw-r--r-- | cogl/cogl-atlas-texture-private.h | 20 | ||||
-rw-r--r-- | cogl/cogl-atlas-texture.c | 290 | ||||
-rw-r--r-- | cogl/cogl-atlas.c | 369 | ||||
-rw-r--r-- | cogl/cogl-atlas.h | 117 | ||||
-rw-r--r-- | cogl/cogl-context-private.h | 8 | ||||
-rw-r--r-- | cogl/cogl-context.c | 29 | ||||
-rw-r--r-- | cogl/cogl-texture-private.h | 15 | ||||
-rw-r--r-- | cogl/cogl-texture.c | 22 | ||||
-rw-r--r-- | cogl/cogl.h | 2 | ||||
-rw-r--r-- | cogl/winsys/cogl-winsys-egl.c | 2 | ||||
-rw-r--r-- | examples/cogl-crate.c | 11 |
19 files changed, 1227 insertions, 520 deletions
diff --git a/cogl-pango/cogl-pango-glyph-cache.c b/cogl-pango/cogl-pango-glyph-cache.c index 04d1e454..39a3f077 100644 --- a/cogl-pango/cogl-pango-glyph-cache.c +++ b/cogl-pango/cogl-pango-glyph-cache.c @@ -26,46 +26,50 @@ * SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <config.h> #include <glib.h> #include "cogl-pango-glyph-cache.h" #include "cogl-pango-private.h" -#include "cogl/cogl-atlas.h" +#include "cogl/cogl-atlas-set.h" #include "cogl/cogl-atlas-texture-private.h" +#include "cogl/cogl-context-private.h" typedef struct _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey; +typedef struct _AtlasClosureState +{ + CoglList list_node; + CoglAtlas *atlas; + CoglAtlasReorganizeClosure *reorganize_closure; + CoglAtlasAllocateClosure *allocate_closure; +} AtlasClosureState; + struct _CoglPangoGlyphCache { CoglContext *ctx; /* Hash table to quickly check whether a particular glyph in a particular font is already cached */ - GHashTable *hash_table; + GHashTable *hash_table; - /* List of CoglAtlases */ - GSList *atlases; + /* Set of CoglAtlases */ + CoglAtlasSet *atlas_set; - /* List of callbacks to invoke when an atlas is reorganized */ - GHookList reorganize_callbacks; + CoglList atlas_closures; - /* TRUE if we've ever stored a texture in the global atlas. This is - used to make sure we only register one callback to listen for - global atlas reorganizations */ - CoglBool using_global_atlas; + /* List of callbacks to invoke when an atlas is reorganized */ + GHookList reorganize_callbacks; /* True if some of the glyphs are dirty. This is used as an optimization in _cogl_pango_glyph_cache_set_dirty_glyphs to avoid iterating the hash table if we know none of them are dirty */ - CoglBool has_dirty_glyphs; + CoglBool has_dirty_glyphs; /* Whether mipmapping is being used for this cache. This only affects whether we decide to put the glyph in the global atlas */ - CoglBool use_mipmapping; + CoglBool use_mipmapping; }; struct _CoglPangoGlyphCacheKey @@ -78,7 +82,10 @@ static void cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value) { if (value->texture) - cogl_object_unref (value->texture); + { + cogl_object_unref (value->texture); + cogl_object_unref (value->atlas); + } g_slice_free (CoglPangoGlyphCacheValue, value); } @@ -117,6 +124,91 @@ cogl_pango_glyph_cache_equal_func (const void *a, const void *b) && key_a->glyph == key_b->glyph; } +static void +atlas_reorganize_cb (CoglAtlas *atlas, void *user_data) +{ + CoglPangoGlyphCache *cache = user_data; + + g_hook_list_invoke (&cache->reorganize_callbacks, FALSE); +} + +static void +allocate_glyph_cb (CoglAtlas *atlas, + CoglTexture *texture, + const CoglAtlasAllocation *allocation, + void *allocation_data, + void *user_data) +{ + CoglPangoGlyphCacheValue *value = allocation_data; + float tex_width, tex_height; + + if (value->texture) + { + cogl_object_unref (value->texture); + cogl_object_unref (value->atlas); + } + value->atlas = cogl_object_ref (atlas); + value->texture = cogl_object_ref (texture); + + tex_width = cogl_texture_get_width (texture); + tex_height = cogl_texture_get_height (texture); + + value->tx1 = allocation->x / tex_width; + value->ty1 = allocation->y / tex_height; + value->tx2 = (allocation->x + value->draw_width) / tex_width; + value->ty2 = (allocation->y + value->draw_height) / tex_height; + + value->tx_pixel = allocation->x; + value->ty_pixel = allocation->y; + + /* The glyph has changed position so it will need to be redrawn */ + value->dirty = TRUE; +} + +static void +atlas_callback (CoglAtlasSet *set, + CoglAtlas *atlas, + CoglAtlasSetEvent event, + void *user_data) +{ + CoglPangoGlyphCache *cache = user_data; + AtlasClosureState *state; + + switch (event) + { + case COGL_ATLAS_SET_EVENT_ADDED: + state = g_slice_new (AtlasClosureState); + state->atlas = atlas; + state->reorganize_closure = + cogl_atlas_add_post_reorganize_callback (atlas, + atlas_reorganize_cb, + cache, + NULL); /* destroy */ + state->allocate_closure = + cogl_atlas_add_allocate_callback (atlas, + allocate_glyph_cb, + cache, + NULL); /* destroy */ + + _cogl_list_insert (cache->atlas_closures.prev, &state->list_node); + break; + case COGL_ATLAS_SET_EVENT_REMOVED: + break; + } +} + +static void +add_global_atlas_cb (CoglAtlas *atlas, + void *user_data) +{ + CoglPangoGlyphCache *cache = user_data; + + atlas_callback (_cogl_get_atlas_set (cache->ctx), + atlas, + COGL_ATLAS_SET_EVENT_ADDED, + cache); +} + CoglPangoGlyphCache * cogl_pango_glyph_cache_new (CoglContext *ctx, CoglBool use_mipmapping) @@ -135,32 +227,47 @@ cogl_pango_glyph_cache_new (CoglContext *ctx, (UDestroyNotify) cogl_pango_glyph_cache_key_free, (UDestroyNotify) cogl_pango_glyph_cache_value_free); - cache->atlases = NULL; + _cogl_list_init (&cache->atlas_closures); + + cache->atlas_set = cogl_atlas_set_new (ctx); + + cogl_atlas_set_set_components (cache->atlas_set, + COGL_TEXTURE_COMPONENTS_A); + + cogl_atlas_set_set_migration_enabled (cache->atlas_set, FALSE); + cogl_atlas_set_set_clear_enabled (cache->atlas_set, TRUE); + + /* We want to be notified when new atlases are added to our local + * atlas set so they can be monitored for being re-arranged... */ + cogl_atlas_set_add_atlas_callback (cache->atlas_set, + atlas_callback, + cache, + NULL); /* destroy */ + + /* We want to be notified when new atlases are added to the global + * atlas set so they can be monitored for being re-arranged... */ + cogl_atlas_set_add_atlas_callback (_cogl_get_atlas_set (ctx), + atlas_callback, + cache, + NULL); /* destroy */ + /* The global atlas set may already have atlases that we will + * want to monitor... */ + cogl_atlas_set_foreach (_cogl_get_atlas_set (ctx), + add_global_atlas_cb, + cache); + g_hook_list_init (&cache->reorganize_callbacks, sizeof (GHook)); cache->has_dirty_glyphs = FALSE; - cache->using_global_atlas = FALSE; - cache->use_mipmapping = use_mipmapping; return cache; } -static void -cogl_pango_glyph_cache_reorganize_cb (void *user_data) -{ - CoglPangoGlyphCache *cache = user_data; - - g_hook_list_invoke (&cache->reorganize_callbacks, FALSE); -} - void cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache) { - g_slist_foreach (cache->atlases, (GFunc) cogl_object_unref, NULL); - g_slist_free (cache->atlases); - cache->atlases = NULL; cache->has_dirty_glyphs = FALSE; g_hash_table_remove_all (cache->hash_table); @@ -169,11 +276,16 @@ cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache) void cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache) { - if (cache->using_global_atlas) + AtlasClosureState *state, *tmp; + + _cogl_list_for_each_safe (state, tmp, &cache->atlas_closures, list_node) { - _cogl_atlas_texture_remove_reorganize_callback ( - cache->ctx, - cogl_pango_glyph_cache_reorganize_cb, cache); + cogl_atlas_remove_post_reorganize_callback (state->atlas, + state->reorganize_closure); + cogl_atlas_remove_allocate_callback (state->atlas, + state->allocate_closure); + _cogl_list_remove (&state->list_node); + g_slice_free (AtlasClosureState, state); } cogl_pango_glyph_cache_clear (cache); @@ -185,33 +297,6 @@ cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache) g_free (cache); } -static void -cogl_pango_glyph_cache_update_position_cb (void *user_data, - CoglTexture *new_texture, - const CoglRectangleMapEntry *rect) -{ - CoglPangoGlyphCacheValue *value = user_data; - float tex_width, tex_height; - - if (value->texture) - cogl_object_unref (value->texture); - value->texture = cogl_object_ref (new_texture); - - tex_width = cogl_texture_get_width (new_texture); - tex_height = cogl_texture_get_height (new_texture); - - value->tx1 = rect->x / tex_width; - value->ty1 = rect->y / tex_height; - value->tx2 = (rect->x + value->draw_width) / tex_width; - value->ty2 = (rect->y + value->draw_height) / tex_height; - - value->tx_pixel = rect->x; - value->ty_pixel = rect->y; - - /* The glyph has changed position so it will need to be redrawn */ - value->dirty = TRUE; -} - static CoglBool cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, PangoFont *font, @@ -246,18 +331,6 @@ cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, value->tx_pixel = 0; value->ty_pixel = 0; - /* The first time we store a texture in the global atlas we'll - register for notifications when the global atlas is reorganized - so we can forward the notification on as a glyph - reorganization */ - if (!cache->using_global_atlas) - { - _cogl_atlas_texture_add_reorganize_callback - (cache->ctx, - cogl_pango_glyph_cache_reorganize_cb, cache); - cache->using_global_atlas = TRUE; - } - return TRUE; } @@ -267,44 +340,17 @@ cogl_pango_glyph_cache_add_to_local_atlas (CoglPangoGlyphCache *cache, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { - CoglAtlas *atlas = NULL; - GSList *l; - - /* Look for an atlas that can reserve the space */ - for (l = cache->atlases; l; l = l->next) - if (_cogl_atlas_reserve_space (l->data, - value->draw_width + 1, - value->draw_height + 1, - value)) - { - atlas = l->data; - break; - } - - /* If we couldn't find one then start a new atlas */ - if (atlas == NULL) - { - atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8, - COGL_ATLAS_CLEAR_TEXTURE | - COGL_ATLAS_DISABLE_MIGRATION, - 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, - value->draw_width + 1, - value->draw_height + 1, - value)) - { - cogl_object_unref (atlas); - return FALSE; - } - - _cogl_atlas_add_reorganize_callback - (atlas, cogl_pango_glyph_cache_reorganize_cb, NULL, cache); - - cache->atlases = g_slist_prepend (cache->atlases, atlas); - } + CoglAtlas *atlas; + + /* Add two pixels for the border + * FIXME: two pixels isn't enough if mipmapping is in use + */ + atlas = cogl_atlas_set_allocate_space (cache->atlas_set, + value->draw_width + 2, + value->draw_height + 2, + value); + if (!atlas) + return FALSE; return TRUE; } @@ -345,12 +391,12 @@ cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, value->dirty = FALSE; else { - /* Try adding the glyph to the global atlas... */ + /* Try adding the glyph to the global atlas set... */ if (!cogl_pango_glyph_cache_add_to_global_atlas (cache, font, glyph, value) && - /* If it fails try the local atlas */ + /* If it fails try the local atlas set */ !cogl_pango_glyph_cache_add_to_local_atlas (cache, font, glyph, diff --git a/cogl-pango/cogl-pango-glyph-cache.h b/cogl-pango/cogl-pango-glyph-cache.h index b65f2628..b922bc10 100644 --- a/cogl-pango/cogl-pango-glyph-cache.h +++ b/cogl-pango/cogl-pango-glyph-cache.h @@ -33,6 +33,7 @@ #include <pango/pango-font.h> #include "cogl/cogl-texture.h" +#include "cogl/cogl-atlas.h" COGL_BEGIN_DECLS @@ -41,6 +42,7 @@ typedef struct _CoglPangoGlyphCacheValue CoglPangoGlyphCacheValue; struct _CoglPangoGlyphCacheValue { + CoglAtlas *atlas; CoglTexture *texture; float tx1; diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 8973bcef..bd1d9256 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -157,7 +157,7 @@ libcogl2_la_LDFLAGS = \ -no-undefined \ -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \ -export-dynamic \ - -export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_primitive_draw|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|test_|unit_test_).*" + -export-symbols-regex "^(cogl|_cogl_list_remove|_cogl_list_insert|_cogl_list_init|_cogl_get_atlas_set|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_primitive_draw|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|test_|unit_test_).*" libcogl2_la_SOURCES = $(cogl_sources_c) nodist_libcogl2_la_SOURCES = $(BUILT_SOURCES) diff --git a/cogl/Makefile.sources b/cogl/Makefile.sources index dedf4081..fd37391e 100644 --- a/cogl/Makefile.sources +++ b/cogl/Makefile.sources @@ -36,7 +36,9 @@ cogl_public_h = \ cogl-renderer.h \ cogl-snippet.h \ cogl-sub-texture.h \ - cogl-atlas-texture.h \ + cogl-atlas-set.h \ + cogl-atlas.h \ + cogl-atlas-texture.h \ cogl-texture-2d-gl.h \ cogl-texture-2d-sliced.h \ cogl-texture-2d.h \ @@ -221,8 +223,10 @@ cogl_sources_c = \ cogl-texture-rectangle.c \ cogl-rectangle-map.h \ cogl-rectangle-map.c \ - cogl-atlas.h \ - cogl-atlas.c \ + cogl-atlas-set-private.h \ + cogl-atlas-set.c \ + cogl-atlas-private.h \ + cogl-atlas.c \ cogl-atlas-texture-private.h \ cogl-atlas-texture.c \ cogl-meta-texture.c \ diff --git a/cogl/cogl-atlas-private.h b/cogl/cogl-atlas-private.h new file mode 100644 index 00000000..a47bb8fb --- /dev/null +++ b/cogl/cogl-atlas-private.h @@ -0,0 +1,88 @@ +/* + * Cogl + * + * A Low-Level GPU Graphics and Utilities API + * + * Copyright (C) 2010,2011 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _COGL_ATLAS_PRIVATE_H_ +#define _COGL_ATLAS_PRIVATE_H_ + +#include "cogl-object-private.h" +#include "cogl-texture.h" +#include "cogl-list.h" +#include "cogl-rectangle-map.h" +#include "cogl-atlas.h" + +typedef enum +{ + COGL_ATLAS_CLEAR_TEXTURE = (1 << 0), + COGL_ATLAS_DISABLE_MIGRATION = (1 << 1) +} CoglAtlasFlags; + +struct _CoglAtlas +{ + CoglObject _parent; + + CoglContext *context; + + CoglRectangleMap *map; + + CoglTexture *texture; + CoglPixelFormat internal_format; + CoglAtlasFlags flags; + + CoglList allocate_closures; + + CoglList pre_reorganize_closures; + CoglList post_reorganize_closures; +}; + +CoglAtlas * +_cogl_atlas_new (CoglContext *context, + CoglPixelFormat internal_format, + CoglAtlasFlags flags); + +CoglBool +_cogl_atlas_allocate_space (CoglAtlas *atlas, + int width, + int height, + void *allocation_data); + +void +_cogl_atlas_remove (CoglAtlas *atlas, + int x, + int y, + int width, + int height); + +CoglTexture * +_cogl_atlas_migrate_allocation (CoglAtlas *atlas, + int x, + int y, + int width, + int height, + CoglPixelFormat internal_format); + +#endif /* _COGL_ATLAS_PRIVATE_H_ */ diff --git a/cogl/cogl-atlas-set-private.h b/cogl/cogl-atlas-set-private.h new file mode 100644 index 00000000..5e3833f7 --- /dev/null +++ b/cogl/cogl-atlas-set-private.h @@ -0,0 +1,58 @@ +/* + * Cogl + * + * A Low-Level GPU Graphics and Utilities API + * + * Copyright (C) 2013,2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#ifndef _COGL_ATLAS_SET_PRIVATE_H_ +#define _COGL_ATLAS_SET_PRIVATE_H_ + +#include <glib.h> + +#include "cogl-atlas.h" +#include "cogl-atlas-set.h" +#include "cogl-list.h" +#include "cogl-object-private.h" + +struct _CoglAtlasSet +{ + CoglObject _parent; + + CoglContext *context; + USList *atlases; + + CoglTextureComponents components; + CoglPixelFormat internal_format; + + CoglList atlas_closures; + + unsigned int clear_enabled : 1; + unsigned int premultiplied : 1; + unsigned int migration_enabled : 1; +}; + +#endif /* _COGL_ATLAS_SET_PRIVATE_H_ */ diff --git a/cogl/cogl-atlas-set.c b/cogl/cogl-atlas-set.c new file mode 100644 index 00000000..b600100d --- /dev/null +++ b/cogl/cogl-atlas-set.c @@ -0,0 +1,269 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2013 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * 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, see + * <http://www.gnu.org/licenses/>. + * + * + */ + +#include <config.h> + +#include "cogl-atlas-set.h" +#include "cogl-atlas-set-private.h" +#include "cogl-atlas-private.h" +#include "cogl-closure-list-private.h" +#include "cogl-texture-private.h" +#include "cogl-error-private.h" + +static void +_cogl_atlas_set_free (CoglAtlasSet *set); + +COGL_OBJECT_DEFINE (AtlasSet, atlas_set); + +static CoglUserDataKey atlas_private_key; + +static void +dissociate_atlases (CoglAtlasSet *set) +{ + USList *l; + + /* NB: The set doesn't maintain a reference on the atlases since we don't + * want to keep them alive if they become empty. */ + for (l = set->atlases; l; l = l->next) + cogl_object_set_user_data (l->data, &atlas_private_key, NULL, NULL); + + u_slist_free (set->atlases); + set->atlases = NULL; +} + +static void +_cogl_atlas_set_free (CoglAtlasSet *set) +{ + dissociate_atlases (set); + + _cogl_closure_list_disconnect_all (&set->atlas_closures); + + u_slice_free (CoglAtlasSet, set); +} + +static void +_update_internal_format (CoglAtlasSet *set) +{ + set->internal_format = _cogl_texture_derive_format (set->context, + COGL_PIXEL_FORMAT_ANY, + set->components, + set->premultiplied); +} + +CoglAtlasSet * +cogl_atlas_set_new (CoglContext *context) +{ + CoglAtlasSet *set = u_slice_new0 (CoglAtlasSet); + + set->context = context; + set->atlases = NULL; + + set->components = COGL_TEXTURE_COMPONENTS_RGBA; + set->premultiplied = TRUE; + _update_internal_format (set); + + set->clear_enabled = FALSE; + set->migration_enabled = TRUE; + + _cogl_list_init (&set->atlas_closures); + + return _cogl_atlas_set_object_new (set); +} + +void +cogl_atlas_set_set_components (CoglAtlasSet *set, + CoglTextureComponents components) +{ + u_return_if_fail (set->atlases == NULL); + + set->components = components; + _update_internal_format (set); +} + +CoglTextureComponents +cogl_atlas_set_get_components (CoglAtlasSet *set) +{ + return set->components; +} + +void +cogl_atlas_set_set_premultiplied (CoglAtlasSet *set, + CoglBool premultiplied) +{ + u_return_if_fail (set->atlases == NULL); + + set->premultiplied = premultiplied; + _update_internal_format (set); +} + +CoglBool +cogl_atlas_set_get_premultiplied (CoglAtlasSet *set) +{ + return set->premultiplied; +} + +void +cogl_atlas_set_set_clear_enabled (CoglAtlasSet *set, + CoglBool clear_enabled) +{ + _COGL_RETURN_IF_FAIL (set->atlases == NULL); + + set->clear_enabled = clear_enabled; +} + +CoglBool +cogl_atlas_set_get_clear_enabled (CoglAtlasSet *set, + CoglBool clear_enabled) +{ + return set->clear_enabled; +} + +void +cogl_atlas_set_set_migration_enabled (CoglAtlasSet *set, + CoglBool migration_enabled) +{ + _COGL_RETURN_IF_FAIL (set->atlases == NULL); + + set->migration_enabled = migration_enabled; +} + +CoglBool +cogl_atlas_set_get_migration_enabled (CoglAtlasSet *set) +{ + return set->migration_enabled; +} + +CoglAtlasSetAtlasClosure * +cogl_atlas_set_add_atlas_callback (CoglAtlasSet *set, + CoglAtlasSetAtlasCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + return _cogl_closure_list_add (&set->atlas_closures, + callback, + user_data, + destroy); +} + +void +cogl_atlas_set_remove_atlas_callback (CoglAtlasSet *set, + CoglAtlasSetAtlasClosure *closure) +{ + _cogl_closure_disconnect (closure); +} + +static void +atlas_destroyed_cb (void *user_data, void *instance) +{ + CoglAtlasSet *set = user_data; + CoglAtlas *atlas = instance; + + set->atlases = u_slist_remove (set->atlases, atlas); +} + +CoglAtlas * +cogl_atlas_set_allocate_space (CoglAtlasSet *set, + int width, + int height, + void *allocation_data) +{ + USList *l; + CoglAtlasFlags flags = 0; + CoglAtlas *atlas; + + /* Look for an existing atlas that can hold the texture */ + for (l = set->atlases; l; l = l->next) + { + if (_cogl_atlas_allocate_space (l->data, width, height, allocation_data)) + return l->data; + } + + if (set->clear_enabled) + flags |= COGL_ATLAS_CLEAR_TEXTURE; + + if (!set->migration_enabled) + flags |= COGL_ATLAS_DISABLE_MIGRATION; + + atlas = _cogl_atlas_new (set->context, + set->internal_format, + flags); + + _cogl_closure_list_invoke (&set->atlas_closures, + CoglAtlasSetAtlasCallback, + set, + atlas, + COGL_ATLAS_SET_EVENT_ADDED); + + COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas); + if (!_cogl_atlas_allocate_space (atlas, width, height, allocation_data)) + { + _cogl_closure_list_invoke (&set->atlas_closures, + CoglAtlasSetAtlasCallback, + set, + atlas, + COGL_ATLAS_SET_EVENT_REMOVED); + + /* Ok, this means we really can't add it to an atlas */ + cogl_object_unref (atlas); + + return NULL; + } + + set->atlases = u_slist_prepend (set->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. set->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 ((CoglObject *)atlas, + &atlas_private_key, + set, + atlas_destroyed_cb); + + /* XXX: whatever allocates space in an atlas set is responsible for + * taking a reference on the corresponding atlas for the allocation + * otherwise. + * + * We want the lifetime of an atlas to be tied to the lifetime of + * the allocations within the atlas so we don't keep a reference + * ourselves. + */ + u_warn_if_fail (atlas->_parent.ref_count != 1); + + cogl_object_unref (atlas); + + return atlas; +} + +void +cogl_atlas_set_foreach (CoglAtlasSet *atlas_set, + CoglAtlasSetForeachCallback callback, + void *user_data) +{ + USList *l; + + for (l = atlas_set->atlases; l; l = l->next) + callback (l->data, user_data); +} diff --git a/cogl/cogl-atlas-set.h b/cogl/cogl-atlas-set.h new file mode 100644 index 00000000..679bd07e --- /dev/null +++ b/cogl/cogl-atlas-set.h @@ -0,0 +1,161 @@ +/* + * Cogl + * + * A Low-Level GPU Graphics and Utilities API + * + * Copyright (C) 2013,2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only <cogl/cogl.h> can be included directly." +#endif + +#ifndef _COGL_ATLAS_SET_H_ +#define _COGL_ATLAS_SET_H_ + +#include <cogl/cogl-types.h> +#include <cogl/cogl-object.h> +#include <cogl/cogl-atlas.h> + +/** + * CoglAtlasSet: + * + * A #CoglAtlasSet represents a set of #CoglAtlas<!-- -->es and a + * #CoglAtlas represents one texture that is sub divided into smaller + * allocations. + * + * After creating a #CoglAtlas you can specify a common format for all + * #CoglAtlas textures that will belong to that set via + * cogl_atlas_set_set_components() and + * cogl_atlas_set_set_premultiplied(). These can't be changed once you + * start allocating from the set. + * + * Two notable properties of a #CoglAtlasSet are whether automatic + * clearing is enabled and whether migration is enabled. + * + * Enabling automatic clearing via cogl_atlas_set_clear_enabled() + * ensures that each new #CoglAtlas texture that's created is + * initialized to contain zeros for all components. Enabling clearing + * can be useful for applications that might end up sampling outside + * the bounds of individual atlas allocations due to filtering so they + * can avoid random values bleeding into samples, resulting in + * artefacts. + * + * When there is not enough room in an atlas texture for a new + * allocation, Cogl will try to allocate a larger texture and then + * migrate the contents of previous allocations to the new, larger + * texture. For images that can easily be re-created and that are + * perhaps only used in an add-hoc fashion it may not be worthwhile + * the cost of migrating the previous allocations. Migration of + * allocations can be disabled via + * cogl_atlas_set_set_migration_enabled(). With migrations disabled + * then previous allocations will be re-allocated space in any + * replacement texture, but no image data will be copied. + */ +typedef struct _CoglAtlasSet CoglAtlasSet; + +/** + * cogl_atlas_set_new: + * @context: A #CoglContext pointer + * + * Return value: A newly allocated #CoglAtlasSet + */ +CoglAtlasSet * +cogl_atlas_set_new (CoglContext *context); + +CoglBool +cogl_is_atlas_set (void *object); + +void +cogl_atlas_set_set_components (CoglAtlasSet *set, + CoglTextureComponents components); + +CoglTextureComponents +cogl_atlas_set_get_components (CoglAtlasSet *set); + +void +cogl_atlas_set_set_premultiplied (CoglAtlasSet *set, + CoglBool premultiplied); + +CoglBool +cogl_atlas_set_get_premultiplied (CoglAtlasSet *set); + +void +cogl_atlas_set_set_clear_enabled (CoglAtlasSet *set, + CoglBool clear_enabled); + +CoglBool +cogl_atlas_set_get_clear_enabled (CoglAtlasSet *set, + CoglBool clear_enabled); + +void +cogl_atlas_set_set_migration_enabled (CoglAtlasSet *set, + CoglBool migration_enabled); + +CoglBool +cogl_atlas_set_get_migration_enabled (CoglAtlasSet *set); + + +void +cogl_atlas_set_clear (CoglAtlasSet *set); + +typedef enum _CoglAtlasSetEvent +{ + COGL_ATLAS_SET_EVENT_ADDED = 1, + COGL_ATLAS_SET_EVENT_REMOVED = 2 +} CoglAtlasSetEvent; + +typedef struct _CoglClosure CoglAtlasSetAtlasClosure; + +typedef void (*CoglAtlasSetAtlasCallback) (CoglAtlasSet *set, + CoglAtlas *atlas, + CoglAtlasSetEvent event, + void *user_data); + +CoglAtlasSetAtlasClosure * +cogl_atlas_set_add_atlas_callback (CoglAtlasSet *set, + CoglAtlasSetAtlasCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); + +void +cogl_atlas_set_remove_atlas_callback (CoglAtlasSet *set, + CoglAtlasSetAtlasClosure *closure); + +CoglAtlas * +cogl_atlas_set_allocate_space (CoglAtlasSet *set, + int width, + int height, + void *allocation_data); + +typedef void (* CoglAtlasSetForeachCallback) (CoglAtlas *atlas, + void *user_data); + +void +cogl_atlas_set_foreach (CoglAtlasSet *atlas_set, + CoglAtlasSetForeachCallback callback, + void *user_data); + +#endif /* _COGL_ATLAS_SET_H_ */ diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h index 6e47cc88..64f6b64f 100644 --- a/cogl/cogl-atlas-texture-private.h +++ b/cogl/cogl-atlas-texture-private.h @@ -34,7 +34,7 @@ #include "cogl-object-private.h" #include "cogl-texture-private.h" #include "cogl-rectangle-map.h" -#include "cogl-atlas.h" +#include "cogl-atlas-set-private.h" #include "cogl-atlas-texture.h" struct _CoglAtlasTexture @@ -48,7 +48,7 @@ struct _CoglAtlasTexture /* The rectangle that was used to add this texture to the atlas. This includes the 1-pixel border */ - CoglRectangleMapEntry rectangle; + CoglAtlasAllocation allocation; /* 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 @@ -61,17 +61,13 @@ struct _CoglAtlasTexture CoglTexture *sub_texture; }; -void -_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, - UHookFunc callback, - void *user_data); - -void -_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, - UHookFunc callback, - void *user_data); - CoglBool _cogl_is_atlas_texture (void *object); +void +_cogl_atlas_texture_atlas_event_handler (CoglAtlasSet *set, + CoglAtlas *atlas, + CoglAtlasSetEvent event, + void *user_data); + #endif /* _COGL_ATLAS_TEXTURE_PRIVATE_H_ */ diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 2eaeb3f9..401801fa 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -31,9 +31,7 @@ * Neil Roberts <neil@linux.intel.com> */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <config.h> #include "cogl-debug.h" #include "cogl-util.h" @@ -47,7 +45,8 @@ #include "cogl-rectangle-map.h" #include "cogl-journal-private.h" #include "cogl-pipeline-opengl-private.h" -#include "cogl-atlas.h" +#include "cogl-atlas-set-private.h" +#include "cogl-atlas-private.h" #include "cogl-sub-texture.h" #include "cogl-error-private.h" #include "cogl-texture-gl-private.h" @@ -63,60 +62,58 @@ static const CoglTextureVtable cogl_atlas_texture_vtable; static CoglSubTexture * _cogl_atlas_texture_create_sub_texture (CoglTexture *full_texture, - const CoglRectangleMapEntry *rectangle) + const CoglAtlasAllocation *allocation) { CoglContext *ctx = full_texture->context; /* Create a subtexture for the given rectangle not including the 1-pixel border */ return cogl_sub_texture_new (ctx, full_texture, - rectangle->x + 1, - rectangle->y + 1, - rectangle->width - 2, - rectangle->height - 2); + allocation->x + 1, + allocation->y + 1, + allocation->width - 2, + allocation->height - 2); } static void -_cogl_atlas_texture_update_position_cb (void *user_data, - CoglTexture *new_texture, - const CoglRectangleMapEntry *rectangle) +_cogl_atlas_texture_allocate_cb (CoglAtlas *atlas, + CoglTexture *texture, + const CoglAtlasAllocation *allocation, + void *allocation_data, + void *user_data) { - CoglAtlasTexture *atlas_tex = user_data; + CoglAtlasTexture *atlas_tex = allocation_data; /* Update the sub texture */ if (atlas_tex->sub_texture) cogl_object_unref (atlas_tex->sub_texture); atlas_tex->sub_texture = COGL_TEXTURE ( - _cogl_atlas_texture_create_sub_texture (new_texture, rectangle)); + _cogl_atlas_texture_create_sub_texture (texture, allocation)); /* Update the position */ - atlas_tex->rectangle = *rectangle; + atlas_tex->allocation = *allocation; + atlas_tex->atlas = cogl_object_ref (atlas); } static void _cogl_atlas_texture_pre_reorganize_foreach_cb - (const CoglRectangleMapEntry *entry, - void *rectangle_data, + (CoglAtlas *atlas, + const CoglAtlasAllocation *allocation, + void *allocation_data, void *user_data) { - CoglAtlasTexture *atlas_tex = rectangle_data; + CoglAtlasTexture *atlas_tex = allocation_data; /* Keep a reference to the texture because we don't want it to be destroyed during the reorganization */ cogl_object_ref (atlas_tex); - - /* Notify cogl-pipeline.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_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex)); } static void -_cogl_atlas_texture_pre_reorganize_cb (void *data) +_cogl_atlas_texture_pre_reorganize_cb (CoglAtlas *atlas, + void *user_data) { - CoglAtlas *atlas = data; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + CoglContext *ctx = user_data; /* We don't know if any journal entries currently depend on OpenGL * texture coordinates that would be invalidated by reorganizing @@ -127,52 +124,50 @@ _cogl_atlas_texture_pre_reorganize_cb (void *data) */ _cogl_flush (ctx); - if (atlas->map) - _cogl_rectangle_map_foreach (atlas->map, - _cogl_atlas_texture_pre_reorganize_foreach_cb, - NULL); + cogl_atlas_foreach (atlas, + _cogl_atlas_texture_pre_reorganize_foreach_cb, + NULL); } typedef struct { CoglAtlasTexture **textures; /* Number of textures found so far */ - unsigned int n_textures; -} CoglAtlasTextureGetRectanglesData; + int n_textures; +} CoglAtlasTextureGetAllocationsData; static void -_cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *entry, - void *rectangle_data, - void *user_data) +_cogl_atlas_texture_get_allocations_cb (CoglAtlas *atlas, + const CoglAtlasAllocation *allocation, + void *allocation_data, + void *user_data) { - CoglAtlasTextureGetRectanglesData *data = user_data; + CoglAtlasTextureGetAllocationsData *data = user_data; - data->textures[data->n_textures++] = rectangle_data; + data->textures[data->n_textures++] = allocation_data; } static void -_cogl_atlas_texture_post_reorganize_cb (void *user_data) +_cogl_atlas_texture_post_reorganize_cb (CoglAtlas *atlas, + void *user_data) { - CoglAtlas *atlas = user_data; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + int n_allocations = cogl_atlas_get_n_allocations (atlas); - if (atlas->map) + if (n_allocations) { - CoglAtlasTextureGetRectanglesData data; - unsigned int i; + CoglAtlasTextureGetAllocationsData data; + int i; - data.textures = u_new (CoglAtlasTexture *, - _cogl_rectangle_map_get_n_rectangles (atlas->map)); + data.textures = u_alloca (sizeof (CoglAtlasTexture *) * n_allocations); data.n_textures = 0; /* We need to remove all of the references that we took during - the preorganize callback. We have to get a separate array of - the textures because CoglRectangleMap doesn't support - removing rectangles during iteration */ - _cogl_rectangle_map_foreach (atlas->map, - _cogl_atlas_texture_get_rectangles_cb, - &data); + * the preorganize callback. We have to get a separate array of + * the textures because CoglAtlas doesn't support removing + * allocations during iteration */ + cogl_atlas_foreach (atlas, + _cogl_atlas_texture_get_allocations_cb, + &data); for (i = 0; i < data.n_textures; i++) { @@ -183,48 +178,7 @@ _cogl_atlas_texture_post_reorganize_cb (void *user_data) if (data.textures[i]->atlas) cogl_object_unref (data.textures[i]); } - - u_free (data.textures); } - - /* Notify any listeners that an atlas has changed */ - u_hook_list_invoke (&ctx->atlas_reorganize_callbacks, FALSE); -} - -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 = u_slist_remove (ctx->atlases, user_data); -} - -static CoglAtlas * -_cogl_atlas_texture_create_atlas (CoglContext *ctx) -{ - static CoglUserDataKey atlas_private_key; - - CoglAtlas *atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888, - 0, - _cogl_atlas_texture_update_position_cb); - - _cogl_atlas_add_reorganize_callback (atlas, - _cogl_atlas_texture_pre_reorganize_cb, - _cogl_atlas_texture_post_reorganize_cb, - atlas); - - ctx->atlases = u_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 @@ -273,7 +227,10 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) if (atlas_tex->atlas) { _cogl_atlas_remove (atlas_tex->atlas, - &atlas_tex->rectangle); + atlas_tex->allocation.x, + atlas_tex->allocation.y, + atlas_tex->allocation.width, + atlas_tex->allocation.height); cogl_object_unref (atlas_tex->atlas); atlas_tex->atlas = NULL; @@ -382,12 +339,12 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) _cogl_flush (ctx); standalone_tex = - _cogl_atlas_copy_rectangle (atlas_tex->atlas, - atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y + 1, - atlas_tex->rectangle.width - 2, - atlas_tex->rectangle.height - 2, - atlas_tex->internal_format); + _cogl_atlas_migrate_allocation (atlas_tex->atlas, + atlas_tex->allocation.x + 1, + atlas_tex->allocation.y + 1, + atlas_tex->allocation.width - 2, + atlas_tex->allocation.height - 2, + atlas_tex->internal_format); /* Note: we simply silently ignore failures to migrate a texture * out (most likely due to lack of memory) and hope for the * best. @@ -459,8 +416,8 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, dst_width, dst_height, bmp, - dst_x + atlas_tex->rectangle.x + 1, - dst_y + atlas_tex->rectangle.y + 1, + dst_x + atlas_tex->allocation.x + 1, + dst_y + atlas_tex->allocation.y + 1, 0, /* level 0 */ error)) return FALSE; @@ -471,20 +428,20 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, src_x, src_y, 1, dst_height, bmp, - atlas_tex->rectangle.x, - dst_y + atlas_tex->rectangle.y + 1, + atlas_tex->allocation.x, + dst_y + atlas_tex->allocation.y + 1, 0, /* level 0 */ error)) return FALSE; /* Update the right edge pixels */ - if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && + if (dst_x + dst_width == atlas_tex->allocation.width - 2 && !cogl_texture_set_region_from_bitmap (atlas->texture, src_x + dst_width - 1, src_y, 1, dst_height, bmp, - atlas_tex->rectangle.x + - atlas_tex->rectangle.width - 1, - dst_y + atlas_tex->rectangle.y + 1, + atlas_tex->allocation.x + + atlas_tex->allocation.width - 1, + dst_y + atlas_tex->allocation.y + 1, 0, /* level 0 */ error)) return FALSE; @@ -494,20 +451,20 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, src_x, src_y, dst_width, 1, bmp, - dst_x + atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y, + dst_x + atlas_tex->allocation.x + 1, + atlas_tex->allocation.y, 0, /* level 0 */ error)) return FALSE; /* Update the bottom edge pixels */ - if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && + if (dst_y + dst_height == atlas_tex->allocation.height - 2 && !cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y + dst_height - 1, dst_width, 1, bmp, - dst_x + atlas_tex->rectangle.x + 1, - atlas_tex->rectangle.y + - atlas_tex->rectangle.height - 1, + dst_x + atlas_tex->allocation.x + 1, + atlas_tex->allocation.y + + atlas_tex->allocation.height - 1, 0, /* level 0 */ error)) return FALSE; @@ -649,6 +606,38 @@ _cogl_atlas_texture_can_use_format (CoglPixelFormat format) format == COGL_PIXEL_FORMAT_RGBA_8888); } +void +_cogl_atlas_texture_atlas_event_handler (CoglAtlasSet *set, + CoglAtlas *atlas, + CoglAtlasSetEvent event, + void *user_data) +{ + switch (event) + { + case COGL_ATLAS_SET_EVENT_ADDED: + { + CoglAtlasReorganizeCallback pre_callback = + _cogl_atlas_texture_pre_reorganize_cb; + CoglAtlasReorganizeCallback post_callback = + _cogl_atlas_texture_post_reorganize_cb; + + cogl_atlas_add_allocate_callback (atlas, + _cogl_atlas_texture_allocate_cb, + NULL, /* user data */ + NULL); /* destroy */ + cogl_atlas_add_pre_reorganize_callback (atlas, pre_callback, + set->context, + NULL); /* destroy */ + cogl_atlas_add_post_reorganize_callback (atlas, post_callback, + set->context, + NULL); /* destroy */ + break; + } + case COGL_ATLAS_SET_EVENT_REMOVED: + break; + } +} + static CoglAtlasTexture * _cogl_atlas_texture_create_base (CoglContext *ctx, int width, @@ -712,7 +701,6 @@ allocate_space (CoglAtlasTexture *atlas_tex, CoglTexture *tex = COGL_TEXTURE (atlas_tex); CoglContext *ctx = tex->context; CoglAtlas *atlas; - USList *l; /* If the texture is in a strange format then we won't use it */ if (!_cogl_atlas_texture_can_use_format (internal_format)) @@ -738,43 +726,24 @@ allocate_space (CoglAtlasTexture *atlas_tex, return FALSE; } - /* 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, - atlas_tex)) - { - cogl_object_ref (atlas); - break; - } - - /* If we couldn't find a suitable atlas then start another */ - if (l == NULL) + /* Add two pixels for the border + * FIXME: two pixels isn't enough if mipmapping is in use + */ + atlas = cogl_atlas_set_allocate_space (ctx->atlas_set, + tex->width + 2, + tex->height + 2, + atlas_tex); + if (!atlas) { - atlas = _cogl_atlas_texture_create_atlas (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, - atlas_tex)) - { - /* Ok, this means we really can't add it to the atlas */ - cogl_object_unref (atlas); - - _cogl_set_error (error, - COGL_SYSTEM_ERROR, - COGL_SYSTEM_ERROR_NO_MEMORY, - "Not enough memory to atlas texture"); - return FALSE; - } + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY, + "Not enough memory to atlas texture"); + return FALSE; } atlas_tex->internal_format = internal_format; - atlas_tex->atlas = atlas; - return TRUE; } @@ -974,31 +943,6 @@ cogl_atlas_texture_new_from_file (CoglContext *ctx, return atlas_tex; } -void -_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, - UHookFunc callback, - void *user_data) -{ - UHook *hook = u_hook_alloc (&ctx->atlas_reorganize_callbacks); - hook->func = callback; - hook->data = user_data; - u_hook_prepend (&ctx->atlas_reorganize_callbacks, hook); -} - -void -_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, - UHookFunc callback, - void *user_data) -{ - UHook *hook = u_hook_find_func_data (&ctx->atlas_reorganize_callbacks, - FALSE, - callback, - user_data); - - if (hook) - u_hook_destroy_link (&ctx->atlas_reorganize_callbacks, hook); -} - static CoglTextureType _cogl_atlas_texture_get_type (CoglTexture *tex) { diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c index fd5a3164..94ae7929 100644 --- a/cogl/cogl-atlas.c +++ b/cogl/cogl-atlas.c @@ -29,11 +29,9 @@ * Neil Roberts <neil@linux.intel.com> */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <config.h> -#include "cogl-atlas.h" +#include "cogl-atlas-private.h" #include "cogl-rectangle-map.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" @@ -50,26 +48,48 @@ static void _cogl_atlas_free (CoglAtlas *atlas); -COGL_OBJECT_INTERNAL_DEFINE (Atlas, atlas); +COGL_OBJECT_DEFINE (Atlas, atlas); CoglAtlas * -_cogl_atlas_new (CoglPixelFormat texture_format, - CoglAtlasFlags flags, - CoglAtlasUpdatePositionCallback update_position_cb) +_cogl_atlas_new (CoglContext *context, + CoglPixelFormat internal_format, + CoglAtlasFlags flags) { CoglAtlas *atlas = u_new (CoglAtlas, 1); - atlas->update_position_cb = update_position_cb; + atlas->context = context; atlas->map = NULL; atlas->texture = NULL; atlas->flags = flags; - atlas->texture_format = texture_format; - u_hook_list_init (&atlas->pre_reorganize_callbacks, sizeof (UHook)); - u_hook_list_init (&atlas->post_reorganize_callbacks, sizeof (UHook)); + atlas->internal_format = internal_format; + + _cogl_list_init (&atlas->allocate_closures); + + _cogl_list_init (&atlas->pre_reorganize_closures); + _cogl_list_init (&atlas->post_reorganize_closures); return _cogl_atlas_object_new (atlas); } +CoglAtlasAllocateClosure * +cogl_atlas_add_allocate_callback (CoglAtlas *atlas, + CoglAtlasAllocateCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + return _cogl_closure_list_add (&atlas->allocate_closures, + callback, + user_data, + destroy); +} + +void +cogl_atlas_remove_allocate_callback (CoglAtlas *atlas, + CoglAtlasAllocateClosure *closure) +{ + _cogl_closure_disconnect (closure); +} + static void _cogl_atlas_free (CoglAtlas *atlas) { @@ -80,8 +100,10 @@ _cogl_atlas_free (CoglAtlas *atlas) if (atlas->map) _cogl_rectangle_map_free (atlas->map); - u_hook_list_clear (&atlas->pre_reorganize_callbacks); - u_hook_list_clear (&atlas->post_reorganize_callbacks); + _cogl_closure_list_disconnect_all (&atlas->allocate_closures); + + _cogl_closure_list_disconnect_all (&atlas->pre_reorganize_closures); + _cogl_closure_list_disconnect_all (&atlas->post_reorganize_closures); u_free (atlas); } @@ -89,21 +111,22 @@ _cogl_atlas_free (CoglAtlas *atlas) typedef struct _CoglAtlasRepositionData { /* The current user data for this texture */ - void *user_data; + void *allocation_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, +_cogl_atlas_migrate (CoglAtlas *atlas, + int n_textures, CoglAtlasRepositionData *textures, - CoglTexture *old_texture, - CoglTexture *new_texture, - void *skip_user_data) + CoglTexture *old_texture, + CoglTexture *new_texture, + void *skip_allocation_data) { - unsigned int i; + int i; CoglBlitData blit_data; /* If the 'disable migrate' flag is set then we won't actually copy @@ -111,19 +134,30 @@ _cogl_atlas_migrate (CoglAtlas *atlas, callback to update the position */ if ((atlas->flags & COGL_ATLAS_DISABLE_MIGRATION)) for (i = 0; i < n_textures; i++) - /* Update the texture position */ - atlas->update_position_cb (textures[i].user_data, - new_texture, - &textures[i].new_position); + { + CoglAtlasAllocation *allocation = + (CoglAtlasAllocation *)&textures[i].new_position; + + /* Update the texture position */ + _cogl_closure_list_invoke (&atlas->allocate_closures, + CoglAtlasAllocateCallback, + atlas, + new_texture, + allocation, + textures[i].allocation_data); + } else { _cogl_blit_begin (&blit_data, new_texture, old_texture); for (i = 0; i < n_textures; i++) { + CoglAtlasAllocation *allocation = + (CoglAtlasAllocation *)&textures[i].new_position; + /* Skip the texture that is being added because it doesn't contain any data yet */ - if (textures[i].user_data != skip_user_data) + if (textures[i].allocation_data != skip_allocation_data) _cogl_blit (&blit_data, textures[i].old_position.x, textures[i].old_position.y, @@ -133,9 +167,12 @@ _cogl_atlas_migrate (CoglAtlas *atlas, textures[i].new_position.height); /* Update the texture position */ - atlas->update_position_cb (textures[i].user_data, + _cogl_closure_list_invoke (&atlas->allocate_closures, + CoglAtlasAllocateCallback, + atlas, new_texture, - &textures[i].new_position); + allocation, + textures[i].allocation_data); } _cogl_blit_end (&blit_data); @@ -146,23 +183,23 @@ typedef struct _CoglAtlasGetRectanglesData { CoglAtlasRepositionData *textures; /* Number of textures found so far */ - unsigned int n_textures; + int n_textures; } CoglAtlasGetRectanglesData; static void _cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle, - void *rect_data, - void *user_data) + 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; + data->textures[data->n_textures++].allocation_data = rect_data; } static void -_cogl_atlas_get_next_size (unsigned int *map_width, - unsigned int *map_height) +_cogl_atlas_get_next_size (int *map_width, + int *map_height) { /* Double the size of the texture by increasing whichever dimension is smaller */ @@ -173,19 +210,18 @@ _cogl_atlas_get_next_size (unsigned int *map_width, } static void -_cogl_atlas_get_initial_size (CoglPixelFormat format, - unsigned int *map_width, - unsigned int *map_height) +_cogl_atlas_get_initial_size (CoglAtlas *atlas, + int *map_width, + int *map_height) { + CoglContext *ctx = atlas->context; unsigned int size; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - ctx->driver_vtable->pixel_format_to_gl (ctx, - format, + atlas->internal_format, &gl_intformat, &gl_format, &gl_type); @@ -195,7 +231,7 @@ _cogl_atlas_get_initial_size (CoglPixelFormat format, initial minimum size. If the format is only 1 byte per pixel we can use 1024x1024, otherwise we'll assume it will take 4 bytes per pixel and use 512x512. */ - if (_cogl_pixel_format_get_bytes_per_pixel (format) == 1) + if (_cogl_pixel_format_get_bytes_per_pixel (atlas->internal_format) == 1) size = 1024; else size = 512; @@ -216,20 +252,19 @@ _cogl_atlas_get_initial_size (CoglPixelFormat format, } static CoglRectangleMap * -_cogl_atlas_create_map (CoglPixelFormat format, - unsigned int map_width, - unsigned int map_height, - unsigned int n_textures, +_cogl_atlas_create_map (CoglAtlas *atlas, + int map_width, + int map_height, + int n_textures, CoglAtlasRepositionData *textures) { + CoglContext *ctx = atlas->context; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; - _COGL_GET_CONTEXT (ctx, NULL); - ctx->driver_vtable->pixel_format_to_gl (ctx, - format, + atlas->internal_format, &gl_intformat, &gl_format, &gl_type); @@ -246,7 +281,7 @@ _cogl_atlas_create_map (CoglPixelFormat format, CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width, map_height, NULL); - unsigned int i; + int i; COGL_NOTE (ATLAS, "Trying to resize the atlas to %ux%u", map_width, map_height); @@ -256,7 +291,7 @@ _cogl_atlas_create_map (CoglPixelFormat format, if (!_cogl_rectangle_map_add (new_atlas, textures[i].old_position.width, textures[i].old_position.height, - textures[i].user_data, + textures[i].allocation_data, &textures[i].new_position)) break; @@ -284,30 +319,29 @@ _cogl_atlas_create_texture (CoglAtlas *atlas, int width, int height) { + CoglContext *ctx = atlas->context; CoglTexture2D *tex; CoglError *ignore_error = NULL; - _COGL_GET_CONTEXT (ctx, NULL); - if ((atlas->flags & COGL_ATLAS_CLEAR_TEXTURE)) { uint8_t *clear_data; CoglBitmap *clear_bmp; - int bpp = _cogl_pixel_format_get_bytes_per_pixel (atlas->texture_format); + int bpp = _cogl_pixel_format_get_bytes_per_pixel (atlas->internal_format); /* Create a buffer of zeroes to initially clear the texture */ clear_data = u_malloc0 (width * height * bpp); clear_bmp = cogl_bitmap_new_for_data (ctx, width, height, - atlas->texture_format, + atlas->internal_format, width * bpp, clear_data); tex = cogl_texture_2d_new_from_bitmap (clear_bmp); _cogl_texture_set_internal_format (COGL_TEXTURE (tex), - atlas->texture_format); + atlas->internal_format); if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) { @@ -325,7 +359,7 @@ _cogl_atlas_create_texture (CoglAtlas *atlas, tex = cogl_texture_2d_new_with_size (ctx, width, height); _cogl_texture_set_internal_format (COGL_TEXTURE (tex), - atlas->texture_format); + atlas->internal_format); if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) { @@ -352,36 +386,24 @@ _cogl_atlas_compare_size_cb (const void *a, return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; } -static void -_cogl_atlas_notify_pre_reorganize (CoglAtlas *atlas) -{ - u_hook_list_invoke (&atlas->pre_reorganize_callbacks, FALSE); -} - -static void -_cogl_atlas_notify_post_reorganize (CoglAtlas *atlas) -{ - u_hook_list_invoke (&atlas->post_reorganize_callbacks, FALSE); -} - CoglBool -_cogl_atlas_reserve_space (CoglAtlas *atlas, - unsigned int width, - unsigned int height, - void *user_data) +_cogl_atlas_allocate_space (CoglAtlas *atlas, + int width, + int height, + void *allocation_data) { CoglAtlasGetRectanglesData data; CoglRectangleMap *new_map; CoglTexture2D *new_tex; - unsigned int map_width, map_height; + int map_width, map_height; CoglBool ret; - CoglRectangleMapEntry new_position; + CoglAtlasAllocation new_allocation; /* 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)) + allocation_data, + (CoglRectangleMapEntry *)&new_allocation)) { COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, @@ -393,9 +415,12 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, 100 / (_cogl_rectangle_map_get_width (atlas->map) * _cogl_rectangle_map_get_height (atlas->map))); - atlas->update_position_cb (user_data, + _cogl_closure_list_invoke (&atlas->allocate_closures, + CoglAtlasAllocateCallback, + atlas, atlas->texture, - &new_position); + &new_allocation, + allocation_data); return TRUE; } @@ -404,7 +429,9 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, we'll notify any users of the atlas that this is going to happen so that for example in CoglAtlasTexture it can notify that the storage has changed and cause a flush */ - _cogl_atlas_notify_pre_reorganize (atlas); + _cogl_closure_list_invoke (&atlas->pre_reorganize_closures, + CoglAtlasReorganizeCallback, + atlas); /* Get an array of all the textures currently in the atlas. */ data.n_textures = 0; @@ -412,7 +439,7 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, data.textures = u_malloc (sizeof (CoglAtlasRepositionData)); else { - unsigned int n_rectangles = + int n_rectangles = _cogl_rectangle_map_get_n_rectangles (atlas->map); data.textures = u_malloc (sizeof (CoglAtlasRepositionData) * (n_rectangles + 1)); @@ -427,7 +454,7 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, 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; + data.textures[data.n_textures++].allocation_data = allocation_data; /* The atlasing algorithm works a lot better if the rectangles are added in decreasing order of size so we'll first sort the @@ -452,10 +479,9 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, _cogl_atlas_get_next_size (&map_width, &map_height); } else - _cogl_atlas_get_initial_size (atlas->texture_format, - &map_width, &map_height); + _cogl_atlas_get_initial_size (atlas, &map_width, &map_height); - new_map = _cogl_atlas_create_map (atlas->texture_format, + new_map = _cogl_atlas_create_map (atlas, map_width, map_height, data.n_textures, data.textures); @@ -500,16 +526,24 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, data.textures, atlas->texture, COGL_TEXTURE (new_tex), - user_data); + allocation_data); _cogl_rectangle_map_free (atlas->map); cogl_object_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, - COGL_TEXTURE (new_tex), - &data.textures[0].new_position); + { + CoglAtlasAllocation *allocation = + (CoglAtlasAllocation *)&data.textures[0].new_position; + + /* We know there's only one texture so we can just directly + update the rectangle from its new position */ + _cogl_closure_list_invoke (&atlas->allocate_closures, + CoglAtlasAllocateCallback, + atlas, + COGL_TEXTURE (new_tex), + allocation, + data.textures[0].allocation_data); + } atlas->map = new_map; atlas->texture = COGL_TEXTURE (new_tex); @@ -530,21 +564,28 @@ _cogl_atlas_reserve_space (CoglAtlas *atlas, u_free (data.textures); - _cogl_atlas_notify_post_reorganize (atlas); + _cogl_closure_list_invoke (&atlas->pre_reorganize_closures, + CoglAtlasReorganizeCallback, + atlas); return ret; } void _cogl_atlas_remove (CoglAtlas *atlas, - const CoglRectangleMapEntry *rectangle) + int x, + int y, + int width, + int height) { - _cogl_rectangle_map_remove (atlas->map, rectangle); + CoglRectangleMapEntry rectangle = { x, y, width, height }; + + _cogl_rectangle_map_remove (atlas->map, &rectangle); COGL_NOTE (ATLAS, "%p: Removed rectangle sized %ix%i", atlas, - rectangle->width, - rectangle->height); + rectangle.width, + rectangle.height); COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _cogl_rectangle_map_get_width (atlas->map), @@ -555,6 +596,12 @@ _cogl_atlas_remove (CoglAtlas *atlas, _cogl_rectangle_map_get_height (atlas->map))); }; +CoglTexture * +cogl_atlas_get_texture (CoglAtlas *atlas) +{ + return atlas->texture; +} + static CoglTexture * create_migration_texture (CoglContext *ctx, int width, @@ -606,19 +653,18 @@ create_migration_texture (CoglContext *ctx, } CoglTexture * -_cogl_atlas_copy_rectangle (CoglAtlas *atlas, - int x, - int y, - int width, - int height, - CoglPixelFormat internal_format) +_cogl_atlas_migrate_allocation (CoglAtlas *atlas, + int x, + int y, + int width, + int height, + CoglPixelFormat internal_format) { + CoglContext *ctx = atlas->context; CoglTexture *tex; CoglBlitData blit_data; CoglError *ignore_error = NULL; - _COGL_GET_CONTEXT (ctx, NULL); - /* Create a new texture at the right size */ tex = create_migration_texture (ctx, width, height, internal_format); if (!cogl_texture_allocate (tex, &ignore_error)) @@ -641,50 +687,91 @@ _cogl_atlas_copy_rectangle (CoglAtlas *atlas, return tex; } +CoglAtlasReorganizeClosure * +cogl_atlas_add_pre_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + _COGL_RETURN_VAL_IF_FAIL (callback != NULL, NULL); + + return _cogl_closure_list_add (&atlas->pre_reorganize_closures, + callback, + user_data, + destroy); +} + void -_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, - UHookFunc pre_callback, - UHookFunc post_callback, - void *user_data) +cogl_atlas_remove_pre_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeClosure *closure) { - if (pre_callback) - { - UHook *hook = u_hook_alloc (&atlas->post_reorganize_callbacks); - hook->func = pre_callback; - hook->data = user_data; - u_hook_prepend (&atlas->pre_reorganize_callbacks, hook); - } - if (post_callback) - { - UHook *hook = u_hook_alloc (&atlas->pre_reorganize_callbacks); - hook->func = post_callback; - hook->data = user_data; - u_hook_prepend (&atlas->post_reorganize_callbacks, hook); - } + _cogl_closure_disconnect (closure); +} + +CoglAtlasReorganizeClosure * +cogl_atlas_add_post_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy) +{ + _COGL_RETURN_VAL_IF_FAIL (callback != NULL, NULL); + + return _cogl_closure_list_add (&atlas->post_reorganize_closures, + callback, + user_data, + destroy); } void -_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, - UHookFunc pre_callback, - UHookFunc post_callback, - void *user_data) +cogl_atlas_remove_post_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeClosure *closure) { - if (pre_callback) - { - UHook *hook = u_hook_find_func_data (&atlas->pre_reorganize_callbacks, - FALSE, - pre_callback, - user_data); - if (hook) - u_hook_destroy_link (&atlas->pre_reorganize_callbacks, hook); - } - if (post_callback) + _cogl_closure_disconnect (closure); +} + +typedef struct _ForeachState +{ + CoglAtlas *atlas; + CoglAtlasForeachCallback callback; + void *user_data; +} ForeachState; + +static void +foreach_rectangle_cb (const CoglRectangleMapEntry *entry, + void *rectangle_data, + void *user_data) +{ + ForeachState *state = user_data; + + state->callback (state->atlas, + (CoglAtlasAllocation *)entry, + rectangle_data, + state->user_data); +} + +void +cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasForeachCallback callback, + void *user_data) +{ + if (atlas->map) { - UHook *hook = u_hook_find_func_data (&atlas->post_reorganize_callbacks, - FALSE, - post_callback, - user_data); - if (hook) - u_hook_destroy_link (&atlas->post_reorganize_callbacks, hook); + ForeachState state; + + state.atlas = atlas; + state.callback = callback; + state.user_data = user_data; + + _cogl_rectangle_map_foreach (atlas->map, foreach_rectangle_cb, &state); } } + +int +cogl_atlas_get_n_allocations (CoglAtlas *atlas) +{ + if (atlas->map) + return _cogl_rectangle_map_get_n_rectangles (atlas->map); + else + return 0; +} + diff --git a/cogl/cogl-atlas.h b/cogl/cogl-atlas.h index 5bba5e74..05f506be 100644 --- a/cogl/cogl-atlas.h +++ b/cogl/cogl-atlas.h @@ -26,80 +26,89 @@ * SOFTWARE. */ -#ifndef __COGL_ATLAS_H -#define __COGL_ATLAS_H +#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) +#error "Only <cogl/cogl.h> can be included directly." +#endif -#include "cogl-rectangle-map.h" -#include "cogl-object-private.h" -#include "cogl-texture.h" +#ifndef _COGL_ATLAS_H_ +#define _COGL_ATLAS_H_ -typedef void -(* CoglAtlasUpdatePositionCallback) (void *user_data, - CoglTexture *new_texture, - const CoglRectangleMapEntry *rect); +#include <cogl/cogl-types.h> +#include <cogl/cogl-object.h> +#include <cogl/cogl-texture.h> -typedef enum +typedef struct _CoglAtlasAllocation { - COGL_ATLAS_CLEAR_TEXTURE = (1 << 0), - COGL_ATLAS_DISABLE_MIGRATION = (1 << 1) -} CoglAtlasFlags; + int x; + int y; + int width; + int height; +} CoglAtlasAllocation; typedef struct _CoglAtlas CoglAtlas; -#define COGL_ATLAS(object) ((CoglAtlas *) object) +/* XXX: Note that during migration _cogl_atlas_get_texture() may not match the + * @texture given here. @texture is more up to date... */ +typedef void +(* CoglAtlasAllocateCallback) (CoglAtlas *atlas, + CoglTexture *texture, + const CoglAtlasAllocation *allocation, + void *allocation_data, + void *user_data); -struct _CoglAtlas -{ - CoglObject _parent; +typedef struct _CoglClosure CoglAtlasAllocateClosure; - CoglRectangleMap *map; +CoglAtlasAllocateClosure * +cogl_atlas_add_allocate_callback (CoglAtlas *atlas, + CoglAtlasAllocateCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); - CoglTexture *texture; - CoglPixelFormat texture_format; - CoglAtlasFlags flags; +void +cogl_atlas_remove_allocate_callback (CoglAtlas *atlas, + CoglAtlasAllocateClosure *closure); - CoglAtlasUpdatePositionCallback update_position_cb; +CoglTexture * +cogl_atlas_get_texture (CoglAtlas *atlas); - UHookList pre_reorganize_callbacks; - UHookList post_reorganize_callbacks; -}; +typedef void (*CoglAtlasForeachCallback) (CoglAtlas *atlas, + const CoglAtlasAllocation *allocation, + void *allocation_data, + void *user_data); +void +cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasForeachCallback callback, + void *user_data); -CoglAtlas * -_cogl_atlas_new (CoglPixelFormat texture_format, - CoglAtlasFlags flags, - CoglAtlasUpdatePositionCallback update_position_cb); +int +cogl_atlas_get_n_allocations (CoglAtlas *atlas); -CoglBool -_cogl_atlas_reserve_space (CoglAtlas *atlas, - unsigned int width, - unsigned int height, - void *user_data); +typedef struct _CoglClosure CoglAtlasReorganizeClosure; -void -_cogl_atlas_remove (CoglAtlas *atlas, - const CoglRectangleMapEntry *rectangle); +typedef void (*CoglAtlasReorganizeCallback) (CoglAtlas *atlas, + void *user_data); -CoglTexture * -_cogl_atlas_copy_rectangle (CoglAtlas *atlas, - int x, - int y, - int width, - int height, - CoglPixelFormat format); +CoglAtlasReorganizeClosure * +cogl_atlas_add_pre_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); void -_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, - UHookFunc pre_callback, - UHookFunc post_callback, - void *user_data); +cogl_atlas_remove_pre_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeClosure *closure); + +CoglAtlasReorganizeClosure * +cogl_atlas_add_post_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeCallback callback, + void *user_data, + CoglUserDataDestroyCallback destroy); void -_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, - UHookFunc pre_callback, - UHookFunc post_callback, - void *user_data); +cogl_atlas_remove_post_reorganize_callback (CoglAtlas *atlas, + CoglAtlasReorganizeClosure *closure); CoglBool -_cogl_is_atlas (void *object); +cogl_is_atlas (void *object); -#endif /* __COGL_ATLAS_H */ +#endif /* _COGL_ATLAS_H_ */ diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h index b89db269..291c6808 100644 --- a/cogl/cogl-context-private.h +++ b/cogl/cogl-context-private.h @@ -45,7 +45,7 @@ #include "cogl-pipeline-private.h" #include "cogl-buffer-private.h" #include "cogl-bitmask.h" -#include "cogl-atlas.h" +#include "cogl-atlas-set.h" #include "cogl-driver.h" #include "cogl-texture-driver.h" #include "cogl-pipeline-cache.h" @@ -212,8 +212,7 @@ struct _CoglContext CoglPipeline *texture_download_pipeline; CoglPipeline *blit_texture_pipeline; - USList *atlases; - UHookList atlas_reorganize_callbacks; + CoglAtlasSet *atlas_set; /* This debugging variable is used to pick a colour for visually displaying the quad batches. It needs to be global so that it can @@ -360,4 +359,7 @@ _cogl_context_get_gl_extensions (CoglContext *context); const char * _cogl_context_get_gl_version (CoglContext *context); +CoglAtlasSet * +_cogl_get_atlas_set (CoglContext *context); + #endif /* __COGL_CONTEXT_PRIVATE_H */ diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index dca21277..747abd67 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -28,9 +28,7 @@ * */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include <config.h> #include "cogl-object.h" #include "cogl-private.h" @@ -47,6 +45,8 @@ #include "cogl-texture-2d-private.h" #include "cogl-texture-3d-private.h" #include "cogl-texture-rectangle-private.h" +#include "cogl-atlas-set.h" +#include "cogl-atlas-texture-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-opengl-private.h" #include "cogl-framebuffer-private.h" @@ -432,9 +432,6 @@ cogl_context_new (CoglDisplay *display, cogl_object_unref (white_pixel_bitmap); - context->atlases = NULL; - u_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (UHook)); - context->buffer_map_fallback_array = u_byte_array_new (); context->buffer_map_fallback_in_use = FALSE; @@ -451,6 +448,14 @@ cogl_context_new (CoglDisplay *display, _cogl_list_init (&context->fences); + context->atlas_set = cogl_atlas_set_new (context); + cogl_atlas_set_set_components (context->atlas_set, COGL_TEXTURE_COMPONENTS_RGBA); + cogl_atlas_set_set_premultiplied (context->atlas_set, FALSE); + cogl_atlas_set_add_atlas_callback (context->atlas_set, + _cogl_atlas_texture_atlas_event_handler, + NULL, /* user data */ + NULL); /* destroy */ + return context; } @@ -461,6 +466,9 @@ _cogl_context_free (CoglContext *context) winsys->context_deinit (context); + if (context->atlas_set) + cogl_object_unref (context->atlas_set); + if (context->default_gl_texture_2d_tex) cogl_object_unref (context->default_gl_texture_2d_tex); if (context->default_gl_texture_3d_tex) @@ -499,9 +507,6 @@ _cogl_context_free (CoglContext *context) if (context->current_clip_stack_valid) _cogl_clip_stack_unref (context->current_clip_stack); - u_slist_free (context->atlases); - u_hook_list_clear (&context->atlas_reorganize_callbacks); - _cogl_bitmask_destroy (&context->enabled_builtin_attributes); _cogl_bitmask_destroy (&context->enable_builtin_attributes_tmp); _cogl_bitmask_destroy (&context->enabled_texcoord_attributes); @@ -711,3 +716,9 @@ cogl_get_clock_time (CoglContext *context) else return 0; } + +CoglAtlasSet * +_cogl_get_atlas_set (CoglContext *context) +{ + return context->atlas_set; +} diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 59567f25..0feaeb01 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -279,9 +279,9 @@ void _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture); /* - * This determines a CoglPixelFormat according to texture::components - * and texture::premultiplied (i.e. the user required components and - * whether the texture should be considered premultiplied) + * This determines a CoglPixelFormat according to @components and + * @premultiplied (i.e. the user required components and whether the + * texture should be considered premultiplied) * * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY) * and wherever possible this function tries to simply return the @@ -291,6 +291,15 @@ _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture); * how to convert a source image in preparation for uploading. */ CoglPixelFormat +_cogl_texture_derive_format (CoglContext *ctx, + CoglPixelFormat src_format, + CoglTextureComponents components, + CoglBool premultiplied); + +/* This is a thin wrapper around _cogl_texture_derive_format + * that simply passes texture->context, texture->components and + * texture->premultiplied in as arguments */ +CoglPixelFormat _cogl_texture_determine_internal_format (CoglTexture *texture, CoglPixelFormat src_format); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 8d8af469..c45509bb 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -1340,18 +1340,18 @@ _cogl_texture_set_internal_format (CoglTexture *texture, } CoglPixelFormat -_cogl_texture_determine_internal_format (CoglTexture *texture, - CoglPixelFormat src_format) +_cogl_texture_derive_format (CoglContext *ctx, + CoglPixelFormat src_format, + CoglTextureComponents components, + CoglBool premultiplied) { - switch (texture->components) + switch (components) { case COGL_TEXTURE_COMPONENTS_DEPTH: if (src_format & COGL_DEPTH_BIT) return src_format; else { - CoglContext *ctx = texture->context; - if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || _cogl_has_private_feature (ctx, @@ -1382,7 +1382,7 @@ _cogl_texture_determine_internal_format (CoglTexture *texture, else format = COGL_PIXEL_FORMAT_RGBA_8888; - if (texture->premultiplied) + if (premultiplied) { if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) return format |= COGL_PREMULT_BIT; @@ -1397,6 +1397,16 @@ _cogl_texture_determine_internal_format (CoglTexture *texture, u_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE); } +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglTexture *texture, + CoglPixelFormat src_format) +{ + return _cogl_texture_derive_format (texture->context, + src_format, + texture->components, + texture->premultiplied); +} + void cogl_texture_set_components (CoglTexture *texture, CoglTextureComponents components) diff --git a/cogl/cogl.h b/cogl/cogl.h index f3f65b2d..b98b3010 100644 --- a/cogl/cogl.h +++ b/cogl/cogl.h @@ -75,6 +75,8 @@ #include <cogl/cogl-texture-3d.h> #include <cogl/cogl-texture-2d-sliced.h> #include <cogl/cogl-sub-texture.h> +#include <cogl/cogl-atlas-set.h> +#include <cogl/cogl-atlas.h> #include <cogl/cogl-atlas-texture.h> #include <cogl/cogl-meta-texture.h> #include <cogl/cogl-primitive-texture.h> diff --git a/cogl/winsys/cogl-winsys-egl.c b/cogl/winsys/cogl-winsys-egl.c index 8fa7f9a1..7b15755b 100644 --- a/cogl/winsys/cogl-winsys-egl.c +++ b/cogl/winsys/cogl-winsys-egl.c @@ -811,7 +811,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, COGL_FRAMEBUFFER (onscreen), COGL_FRAMEBUFFER_STATE_BIND); - if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage) + if (egl_renderer->pf_eglSwapBuffersWithDamage) { CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); size_t size = n_rectangles * sizeof (int) * 4; diff --git a/examples/cogl-crate.c b/examples/cogl-crate.c index 4eeab9d9..985ac370 100644 --- a/examples/cogl-crate.c +++ b/examples/cogl-crate.c @@ -284,8 +284,17 @@ main (int argc, char **argv) if (data.swap_ready) { + static gboolean swapped = FALSE; + int rect[4] = { 0, 0, 320, 240 }; + paint (&data); - cogl_onscreen_swap_buffers (COGL_ONSCREEN (fb)); + if (!swapped) + { + cogl_onscreen_swap_buffers (COGL_ONSCREEN (fb)); + swapped = TRUE; + } + else + cogl_onscreen_swap_buffers_with_damage (COGL_ONSCREEN (fb), rect, 1); } cogl_poll_renderer_get_info (cogl_context_get_renderer (ctx), |