[gtk/wip/matthiasc/shared-glyph-cache] Move towards a unified cache
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/matthiasc/shared-glyph-cache] Move towards a unified cache
- Date: Tue, 4 Jun 2019 17:39:49 +0000 (UTC)
commit 6c3154da7750018fd2b324c46f75684fb95507c7
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Jun 4 11:36:38 2019 +0000
Move towards a unified cache
Share the glyph and icon caches, and add a way
to opt out of sharing the caches via the
GSK_NO_SHARED_CACHES env var.
gsk/gl/gskglglyphcache.c | 329 +++++++++++++-------------------------
gsk/gl/gskglglyphcacheprivate.h | 17 +-
gsk/gl/gskgliconcache.c | 146 ++++++-----------
gsk/gl/gskgliconcacheprivate.h | 10 +-
gsk/gl/gskglrenderer.c | 56 +++++--
gsk/gl/gskgltextureatlas.c | 165 +++++++++++++++++++
gsk/gl/gskgltextureatlasprivate.h | 17 ++
7 files changed, 398 insertions(+), 342 deletions(-)
---
diff --git a/gsk/gl/gskglglyphcache.c b/gsk/gl/gskglglyphcache.c
index 816db7607e..5d57130880 100644
--- a/gsk/gl/gskglglyphcache.c
+++ b/gsk/gl/gskglglyphcache.c
@@ -48,18 +48,9 @@ create_atlas (GskGLGlyphCache *self,
return atlas;
}
-static void
-free_atlas (gpointer v)
-{
- GskGLTextureAtlas *atlas = v;
-
- gsk_gl_texture_atlas_free (atlas);
-
- g_free (atlas);
-}
-
GskGLGlyphCache *
-gsk_gl_glyph_cache_new (GdkDisplay *display)
+gsk_gl_glyph_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
GskGLGlyphCache *glyph_cache;
@@ -68,31 +59,36 @@ gsk_gl_glyph_cache_new (GdkDisplay *display)
glyph_cache->display = display;
glyph_cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
glyph_cache_key_free, glyph_cache_value_free);
- glyph_cache->atlases = g_ptr_array_new_with_free_func (free_atlas);
+
+ glyph_cache->atlases = gsk_gl_texture_atlases_ref (atlases);
+
+ glyph_cache->ref_count = 1;
return glyph_cache;
}
+GskGLGlyphCache *
+gsk_gl_glyph_cache_ref (GskGLGlyphCache *self)
+{
+ self->ref_count++;
+
+ return self;
+}
+
void
-gsk_gl_glyph_cache_free (GskGLGlyphCache *self)
+gsk_gl_glyph_cache_unref (GskGLGlyphCache *self)
{
- guint i;
+ g_assert (self->ref_count > 0);
- for (i = 0; i < self->atlases->len; i ++)
+ if (self->ref_count == 1)
{
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->texture_id != 0)
- {
- glDeleteTextures (1, &atlas->texture_id);
- atlas->texture_id = 0;
- }
+ gsk_gl_texture_atlases_unref (self->atlases);
+ g_hash_table_unref (self->hash_table);
+ g_free (self);
+ return;
}
- g_ptr_array_unref (self->atlases);
- g_hash_table_unref (self->hash_table);
-
- g_free (self);
+ self->ref_count--;
}
static gboolean
@@ -129,83 +125,11 @@ glyph_cache_value_free (gpointer v)
g_free (v);
}
-static void
-add_to_cache (GskGLGlyphCache *cache,
- GlyphCacheKey *key,
- GskGLCachedGlyph *value)
-{
- const int width = value->draw_width * key->scale / 1024;
- const int height = value->draw_height * key->scale / 1024;
- GskGLTextureAtlas *atlas = NULL;
- guint i, p;
- int packed_x, packed_y;
-
- /* Try all the atlases and pick the first one that can hold
- * our new glyph */
- for (i = 0, p = cache->atlases->len; i < p; i ++)
- {
- GskGLTextureAtlas *test_atlas = g_ptr_array_index (cache->atlases, i);
- gboolean was_packed;
-
- was_packed = gsk_gl_texture_atlas_pack (test_atlas, width + 2, height + 2, &packed_x, &packed_y);
-
- if (was_packed)
- {
- atlas = test_atlas;
- break;
- }
- }
-
- if (atlas == NULL)
- {
- gboolean was_packed;
-
- atlas = create_atlas (cache, width + 2, height + 2);
-
- g_ptr_array_add (cache->atlases, atlas);
-
- was_packed = gsk_gl_texture_atlas_pack (atlas,
- width + 2, height + 2,
- &packed_x, &packed_y);
-
- g_assert (was_packed);
- }
-
- value->tx = (float)(packed_x + 1) / atlas->width;
- value->ty = (float)(packed_y + 1) / atlas->height;
- value->tw = (float)width / atlas->width;
- value->th = (float)height / atlas->height;
- value->used = TRUE;
-
- value->atlas = atlas;
-
- if (atlas->user_data == NULL)
- atlas->user_data = g_new0 (DirtyGlyph, 1);
-
- ((DirtyGlyph *)atlas->user_data)->key = key;
- ((DirtyGlyph *)atlas->user_data)->value = value;
-
-#ifdef G_ENABLE_DEBUG
- if (GSK_DEBUG_CHECK (GLYPH_CACHE))
- {
- for (i = 0; i < cache->atlases->len; i++)
- {
- atlas = g_ptr_array_index (cache->atlases, i);
- g_message ("atlas %d (%dx%d): %.2g%% old pixels",
- i, atlas->width, atlas->height,
- gsk_gl_texture_atlas_get_unused_ratio (atlas));
- }
- }
-#endif
-}
-
static gboolean
-render_glyph (const GskGLTextureAtlas *atlas,
- const DirtyGlyph *glyph,
- GskImageRegion *region)
+render_glyph (GlyphCacheKey *key,
+ GskGLCachedGlyph *value,
+ GskImageRegion *region)
{
- GlyphCacheKey *key = glyph->key;
- GskGLCachedGlyph *value = glyph->value;
cairo_surface_t *surface;
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
@@ -222,9 +146,8 @@ render_glyph (const GskGLTextureAtlas *atlas,
surface_width = value->draw_width * key->scale / 1024;
surface_height = value->draw_height * key->scale / 1024;
- /* TODO: Give glyphs that large their own texture in the proper size. Don't
- * put them in the atlas at all. */
- if (surface_width > atlas->width || surface_height > atlas->height)
+ /* XXX should never happen */
+ if (surface_width > value->atlas->width || surface_height > value->atlas->height)
return FALSE;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, surface_width);
@@ -259,17 +182,17 @@ render_glyph (const GskGLTextureAtlas *atlas,
region->height = cairo_image_surface_get_height (surface);
region->stride = cairo_image_surface_get_stride (surface);
region->data = data;
- region->x = (gsize)(value->tx * atlas->width);
- region->y = (gsize)(value->ty * atlas->height);
+ region->x = (gsize)(value->tx * value->atlas->width);
+ region->y = (gsize)(value->ty * value->atlas->height);
cairo_surface_destroy (surface);
+
return TRUE;
}
static void
-upload_region_or_else (GskGLGlyphCache *self,
- guint texture_id,
- GskImageRegion *region)
+upload_region (guint texture_id,
+ GskImageRegion *region)
{
glBindTexture (GL_TEXTURE_2D, texture_id);
glTextureSubImage2D (texture_id, 0, region->x, region->y, region->width, region->height,
@@ -277,29 +200,90 @@ upload_region_or_else (GskGLGlyphCache *self,
}
static void
-upload_dirty_glyph (GskGLGlyphCache *self,
- GskGLTextureAtlas *atlas)
+upload_glyph (GskGLGlyphCache *self,
+ GlyphCacheKey *key,
+ GskGLCachedGlyph *value)
{
GskImageRegion region;
- g_assert (atlas->user_data != NULL);
-
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading glyph %d",
- ((DirtyGlyph *)atlas->user_data)->key->glyph);
+ key->glyph);
- if (render_glyph (atlas, (DirtyGlyph *)atlas->user_data, ®ion))
+ if (render_glyph (key, value, ®ion))
{
- upload_region_or_else (self, atlas->texture_id, ®ion);
+ upload_region (value->atlas->texture_id, ®ion);
g_free (region.data);
}
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
+}
+
+static void
+add_to_cache (GskGLGlyphCache *self,
+ GlyphCacheKey *key,
+ GskGLCachedGlyph *value)
+{
+ const int width = value->draw_width * key->scale / 1024;
+ const int height = value->draw_height * key->scale / 1024;
+ GskGLTextureAtlas *atlas = NULL;
+ guint i, p;
+ int packed_x, packed_y;
+ GPtrArray *atlases = self->atlases->atlases;
+
+ /* Try all the atlases and pick the first one that can hold
+ * our new glyph */
+ for (i = 0, p = atlases->len; i < p; i ++)
+ {
+ GskGLTextureAtlas *test_atlas = g_ptr_array_index (atlases, i);
+ gboolean was_packed;
+
+ was_packed = gsk_gl_texture_atlas_pack (test_atlas, width + 2, height + 2, &packed_x, &packed_y);
+
+ if (was_packed)
+ {
+ atlas = test_atlas;
+ break;
+ }
+ }
+
+ if (atlas == NULL)
+ {
+ gboolean was_packed;
+
+ atlas = create_atlas (self, width + 2, height + 2);
+
+ g_ptr_array_add (atlases, atlas);
- /* TODO: This could be unnecessary. We can just reuse the allocated
- * DirtyGlyph next time.
- */
- g_clear_pointer (&atlas->user_data, g_free);
+ was_packed = gsk_gl_texture_atlas_pack (atlas,
+ width + 2, height + 2,
+ &packed_x, &packed_y);
+
+ g_assert (was_packed);
+ }
+
+ value->tx = (float)(packed_x + 1) / atlas->width;
+ value->ty = (float)(packed_y + 1) / atlas->height;
+ value->tw = (float)width / atlas->width;
+ value->th = (float)height / atlas->height;
+ value->used = TRUE;
+
+ value->atlas = atlas;
+
+ upload_glyph (self, key, value);
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_DEBUG_CHECK (GLYPH_CACHE))
+ {
+ for (i = 0; i < atlases->len; i++)
+ {
+ atlas = g_ptr_array_index (atlases, i);
+ g_message ("atlas %d (%dx%d): %.2g%% old pixels",
+ i, atlas->width, atlas->height,
+ gsk_gl_texture_atlas_get_unused_ratio (atlas));
+ }
+ }
+#endif
}
const GskGLCachedGlyph *
@@ -370,66 +354,19 @@ gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
return value;
}
-/* Not using gdk_gl_driver_create_texture here, since we want
- * this texture to survive the driver and stay around until
- * the display gets closed.
- */
-static guint
-create_shared_texture (GskGLGlyphCache *self,
- int width,
- int height)
-{
- guint texture_id;
-
- glGenTextures (1, &texture_id);
- glBindTexture (GL_TEXTURE_2D, texture_id);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- else
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
- glBindTexture (GL_TEXTURE_2D, 0);
-
- return texture_id;
-}
-
guint
gsk_gl_glyph_cache_get_glyph_texture_id (GskGLGlyphCache *self,
const GskGLCachedGlyph *glyph)
{
- GskGLTextureAtlas *atlas = glyph->atlas;
-
- g_assert (atlas != NULL);
-
- if (atlas->texture_id == 0)
- {
- atlas->texture_id = create_shared_texture (self, atlas->width, atlas->height);
- gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
- GL_TEXTURE, atlas->texture_id,
- "Glyph atlas %d", atlas->texture_id);
- }
-
- if (atlas->user_data != NULL)
- upload_dirty_glyph (self, atlas);
-
- return atlas->texture_id;
+ return glyph->atlas->texture_id;
}
void
gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
{
- int i;
GHashTableIter iter;
GlyphCacheKey *key;
GskGLCachedGlyph *value;
- GHashTable *removed = g_hash_table_new (g_direct_hash, g_direct_equal);
guint dropped = 0;
self->timestamp++;
@@ -437,45 +374,12 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
if ((self->timestamp - 1) % CHECK_INTERVAL != 0)
return;
- /* look for atlases to drop, and create a mapping of updated texture indices */
- for (i = self->atlases->len - 1; i >= 0; i--)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
- {
- GSK_NOTE(GLYPH_CACHE,
- g_message ("Dropping atlas %d (%g.2%% old)", i,
- gsk_gl_texture_atlas_get_unused_ratio (atlas)));
-
-#if 0
- static int kk;
-
- g_message ("Dropping glyph cache... Ratio: %f",
- gsk_gl_texture_atlas_get_unused_ratio (atlas));
- gsk_gl_image_write_to_png (&atlas->image, driver,
- g_strdup_printf ("dropped_%d.png", kk++));
-#endif
-
- if (atlas->texture_id != 0)
- {
- glDeleteTextures (1, &atlas->texture_id);
- atlas->texture_id = 0;
- }
-
- g_hash_table_add (removed, atlas);
-
- g_ptr_array_remove_index (self->atlases, i);
- }
- }
-
- /* Remove all glyphs whose atlas was removed, and
- * mark old glyphs as unused
- */
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
- if (g_hash_table_contains (removed, value->atlas))
+ guint pos;
+
+ if (!g_ptr_array_find (self->atlases->atlases, value->atlas, &pos))
{
g_hash_table_iter_remove (&iter);
dropped++;
@@ -496,23 +400,6 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
}
}
}
- g_hash_table_unref (removed);
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d glyphs", dropped));
-
-#if 0
- for (i = 0; i < self->atlases->len; i++)
- {
- GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->texture_id)
- {
- char *filename;
-
- filename = g_strdup_printf ("glyphatlas%d-%ld.png", i, self->timestamp);
- gsk_gl_image_write_to_png (atlas->image, driver, filename);
- g_free (filename);
- }
- }
-#endif
}
diff --git a/gsk/gl/gskglglyphcacheprivate.h b/gsk/gl/gskglglyphcacheprivate.h
index 5d60a38c64..f668646b52 100644
--- a/gsk/gl/gskglglyphcacheprivate.h
+++ b/gsk/gl/gskglglyphcacheprivate.h
@@ -9,9 +9,11 @@
typedef struct
{
+ int ref_count;
+
GdkDisplay *display;
GHashTable *hash_table;
- GPtrArray *atlases;
+ GskGLTextureAtlases *atlases;
guint64 timestamp;
} GskGLGlyphCache;
@@ -23,15 +25,8 @@ typedef struct
guint scale; /* times 1024 */
} GlyphCacheKey;
-typedef struct _DirtyGlyph DirtyGlyph;
typedef struct _GskGLCachedGlyph GskGLCachedGlyph;
-struct _DirtyGlyph
-{
- GlyphCacheKey *key;
- GskGLCachedGlyph *value;
-};
-
struct _GskGLCachedGlyph
{
GskGLTextureAtlas *atlas;
@@ -53,8 +48,10 @@ struct _GskGLCachedGlyph
};
-GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display);
-void gsk_gl_glyph_cache_free (GskGLGlyphCache *self);
+GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases);
+GskGLGlyphCache * gsk_gl_glyph_cache_ref (GskGLGlyphCache *self);
+void gsk_gl_glyph_cache_unref (GskGLGlyphCache *self);
void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self);
guint gsk_gl_glyph_cache_get_glyph_texture_id (GskGLGlyphCache *self,
const GskGLCachedGlyph *glyph);
diff --git a/gsk/gl/gskgliconcache.c b/gsk/gl/gskgliconcache.c
index 04869d388c..d85afc926e 100644
--- a/gsk/gl/gskgliconcache.c
+++ b/gsk/gl/gskgliconcache.c
@@ -23,56 +23,49 @@ icon_data_free (gpointer p)
g_free (p);
}
-static void
-free_atlas (gpointer v)
-{
- GskGLTextureAtlas *atlas = v;
-
- gsk_gl_texture_atlas_free (atlas);
-
- g_free (atlas);
-}
-
GskGLIconCache *
-gsk_gl_icon_cache_new (GdkDisplay *display)
+gsk_gl_icon_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
GskGLIconCache *self;
self = g_new0 (GskGLIconCache, 1);
self->display = display;
-
- self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)free_atlas);
self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
+ self->atlases = gsk_gl_texture_atlases_ref (atlases);
+ self->ref_count = 1;
+
+ return self;
+}
+
+GskGLIconCache *
+gsk_gl_icon_cache_ref (GskGLIconCache *self)
+{
+ self->ref_count++;
return self;
}
void
-gsk_gl_icon_cache_free (GskGLIconCache *self)
+gsk_gl_icon_cache_unref (GskGLIconCache *self)
{
- guint i, p;
+ g_assert (self->ref_count > 0);
- for (i = 0, p = self->atlases->len; i < p; i ++)
+ if (self->ref_count == 1)
{
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->texture_id != 0)
- {
- glDeleteTextures (1, &atlas->texture_id);
- atlas->texture_id = 0;
- }
+ gsk_gl_texture_atlases_unref (self->atlases);
+ g_hash_table_unref (self->icons);
+ g_free (self);
+ return;
}
- g_ptr_array_free (self->atlases, TRUE);
- g_hash_table_unref (self->icons);
- g_free (self);
+ self->ref_count--;
}
void
gsk_gl_icon_cache_begin_frame (GskGLIconCache *self)
{
- gint i, p;
GHashTableIter iter;
GdkTexture *texture;
IconData *icon_data;
@@ -81,45 +74,30 @@ gsk_gl_icon_cache_begin_frame (GskGLIconCache *self)
g_hash_table_iter_init (&iter, self->icons);
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
{
- icon_data->frame_age ++;
+ guint pos;
- if (icon_data->frame_age > MAX_FRAME_AGE)
+ if (!g_ptr_array_find (self->atlases->atlases, icon_data->atlas, &pos))
{
-
- if (icon_data->used)
- {
- const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
- const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
-
- gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
- icon_data->used = FALSE;
- }
- /* We do NOT remove the icon here. Instead, We wait until we drop the entire atlas.
- * This way we can revive it when we use it again. */
+ g_hash_table_iter_remove (&iter);
}
- }
-
- for (i = 0, p = self->atlases->len; i < p; i ++)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_UNUSED_RATIO)
+ else
{
- g_hash_table_iter_init (&iter, self->icons);
- while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
- {
- if (icon_data->atlas == atlas)
- g_hash_table_iter_remove (&iter);
- }
+ icon_data->frame_age ++;
- if (atlas->texture_id != 0)
+ if (icon_data->frame_age > MAX_FRAME_AGE)
{
- glDeleteTextures (1, &atlas->texture_id);
- atlas->texture_id = 0;
- }
- g_ptr_array_remove_index_fast (self->atlases, i);
- i --; /* Check the current index again */
+ if (icon_data->used)
+ {
+ const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
+ const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
+
+ gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
+ icon_data->used = FALSE;
+ }
+ /* We do NOT remove the icon here. Instead, We wait until we drop the entire atlas.
+ * This way we can revive it when we use it again. */
+ }
}
}
}
@@ -154,32 +132,6 @@ pad_surface (cairo_surface_t *surface)
return padded;
}
-static guint
-create_shared_texture (GskGLIconCache *self,
- int width,
- int height)
-{
- guint texture_id;
-
- glGenTextures (1, &texture_id);
- glBindTexture (GL_TEXTURE_2D, texture_id);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- else
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
- glBindTexture (GL_TEXTURE_2D, 0);
-
- return texture_id;
-}
-
static void
upload_region_or_else (GskGLIconCache *self,
guint texture_id,
@@ -203,8 +155,8 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
icon_data->frame_age = 0;
if (!icon_data->used)
{
- const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
- const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
+ const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
+ const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
gsk_gl_texture_atlas_mark_used (icon_data->atlas, w + 2, h + 2);
icon_data->used = TRUE;
@@ -225,13 +177,14 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GskImageRegion region;
cairo_surface_t *surface;
cairo_surface_t *padded_surface;
+ GPtrArray *atlases = self->atlases->atlases;
g_assert (twidth < ATLAS_SIZE);
g_assert (theight < ATLAS_SIZE);
- for (i = 0, p = self->atlases->len; i < p; i ++)
+ for (i = 0, p = atlases->len; i < p; i ++)
{
- atlas = g_ptr_array_index (self->atlases, i);
+ atlas = g_ptr_array_index (atlases, i);
if (gsk_gl_texture_atlas_pack (atlas, twidth + 2, theight + 2, &packed_x, &packed_y))
{
@@ -248,17 +201,14 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
/* No atlas has enough space, so create a new one... */
atlas = g_malloc (sizeof (GskGLTextureAtlas));
gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE);
- atlas->texture_id = create_shared_texture (self, atlas->width, atlas->height);
- gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
- GL_TEXTURE, atlas->texture_id,
- "Icon atlas %d", atlas->texture_id);
+ gsk_gl_texture_atlas_realize (atlas);
/* Pack it onto that one, which surely has enought space... */
gsk_gl_texture_atlas_pack (atlas, twidth + 2, theight + 2, &packed_x, &packed_y);
packed_x += 1;
packed_y += 1;
- g_ptr_array_add (self->atlases, atlas);
+ g_ptr_array_add (atlases, atlas);
}
icon_data = g_new0 (IconData, 1);
@@ -266,10 +216,10 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
icon_data->frame_age = 0;
icon_data->used = TRUE;
graphene_rect_init (&icon_data->texture_rect,
- (float)packed_x / ATLAS_SIZE,
- (float)packed_y / ATLAS_SIZE,
- (float)twidth / ATLAS_SIZE,
- (float)theight / ATLAS_SIZE);
+ (float)packed_x / atlas->width,
+ (float)packed_y / atlas->height,
+ (float)twidth / atlas->width,
+ (float)theight / atlas->height);
g_hash_table_insert (self->icons, texture, icon_data);
diff --git a/gsk/gl/gskgliconcacheprivate.h b/gsk/gl/gskgliconcacheprivate.h
index 36f30087fa..ade371b930 100644
--- a/gsk/gl/gskgliconcacheprivate.h
+++ b/gsk/gl/gskgliconcacheprivate.h
@@ -10,16 +10,20 @@
typedef struct
{
+ int ref_count;
+
GdkDisplay *display;
GskGLDriver *gl_driver;
- GPtrArray *atlases;
+ GskGLTextureAtlases *atlases;
GHashTable *icons; /* GdkTexture -> IconData */
} GskGLIconCache;
-GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display);
-void gsk_gl_icon_cache_free (GskGLIconCache *self);
+GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases);
+GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self);
+void gsk_gl_icon_cache_unref (GskGLIconCache *self);
void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self);
void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index a090340f59..33f0decbc7 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -335,6 +335,7 @@ struct _GskGLRenderer
RenderOpBuilder op_builder;
GArray *render_ops;
+ GskGLTextureAtlases *atlases;
GskGLGlyphCache *glyph_cache;
GskGLIconCache *icon_cache;
GskGLShadowCache shadow_cache;
@@ -2464,31 +2465,63 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
return TRUE;
}
+static GskGLTextureAtlases *
+get_texture_atlases_for_display (GdkDisplay *display)
+{
+ GskGLTextureAtlases *atlases;
+
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_texture_atlases_new ();
+
+ atlases = (GskGLTextureAtlases*)g_object_get_data (G_OBJECT (display), "gl-texture-atlases");
+ if (atlases == NULL)
+ {
+ atlases = gsk_gl_texture_atlases_new ();
+ g_object_set_data_full (G_OBJECT (display), "gl-texture-atlases",
+ gsk_gl_texture_atlases_ref (atlases),
+ (GDestroyNotify) gsk_gl_texture_atlases_unref);
+ }
+
+ return atlases;
+}
+
static GskGLGlyphCache *
-get_glyph_cache_for_display (GdkDisplay *display)
+get_glyph_cache_for_display (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
GskGLGlyphCache *glyph_cache;
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_glyph_cache_new (display, atlases);
+
glyph_cache = (GskGLGlyphCache*)g_object_get_data (G_OBJECT (display), "gl-glyph-cache");
if (glyph_cache == NULL)
{
- glyph_cache = gsk_gl_glyph_cache_new (display);
- g_object_set_data_full (G_OBJECT (display), "gl-glyph-cache", glyph_cache, (GDestroyNotify)
gsk_gl_glyph_cache_free);
+ glyph_cache = gsk_gl_glyph_cache_new (display, atlases);
+ g_object_set_data_full (G_OBJECT (display), "gl-glyph-cache",
+ gsk_gl_glyph_cache_ref (glyph_cache),
+ (GDestroyNotify) gsk_gl_glyph_cache_unref);
}
return glyph_cache;
}
static GskGLIconCache *
-get_icon_cache_for_display (GdkDisplay *display)
+get_icon_cache_for_display (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
GskGLIconCache *icon_cache;
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_icon_cache_new (display, atlases);
+
icon_cache = (GskGLIconCache*)g_object_get_data (G_OBJECT (display), "gl-icon-cache");
if (icon_cache == NULL)
{
- icon_cache = gsk_gl_icon_cache_new (display);
- g_object_set_data_full (G_OBJECT (display), "gl-icon-cache", icon_cache, (GDestroyNotify)
gsk_gl_icon_cache_free);
+ icon_cache = gsk_gl_icon_cache_new (display, atlases);
+ g_object_set_data_full (G_OBJECT (display), "gl-icon-cache",
+ gsk_gl_icon_cache_ref (icon_cache),
+ (GDestroyNotify) gsk_gl_icon_cache_unref);
}
return icon_cache;
@@ -2524,8 +2557,9 @@ gsk_gl_renderer_realize (GskRenderer *renderer,
if (!gsk_gl_renderer_create_programs (self, error))
return FALSE;
- self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface));
- self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface));
+ self->atlases = get_texture_atlases_for_display (gdk_surface_get_display (surface));
+ self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases);
+ self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases);
gsk_gl_shadow_cache_init (&self->shadow_cache);
return TRUE;
@@ -2550,8 +2584,9 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
for (i = 0; i < GL_N_PROGRAMS; i ++)
glDeleteProgram (self->programs[i].id);
- self->glyph_cache = NULL;
- self->icon_cache = NULL;
+ g_clear_pointer (&self->glyph_cache, gsk_gl_glyph_cache_unref);
+ g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref);
+ g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref);
gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
g_clear_object (&self->gl_profiler);
@@ -3117,6 +3152,7 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
ORTHO_FAR_PLANE);
graphene_matrix_scale (&projection, 1, -1, 1);
+ gsk_gl_texture_atlases_begin_frame (self->atlases);
gsk_gl_glyph_cache_begin_frame (self->glyph_cache);
gsk_gl_icon_cache_begin_frame (self->icon_cache);
gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
diff --git a/gsk/gl/gskgltextureatlas.c b/gsk/gl/gskgltextureatlas.c
index 7befcfbbe3..1c59b99a1d 100644
--- a/gsk/gl/gskgltextureatlas.c
+++ b/gsk/gl/gskgltextureatlas.c
@@ -1,6 +1,123 @@
+#include "config.h"
#include "gskgltextureatlasprivate.h"
+#include "gskdebugprivate.h"
+#include "gdkglcontextprivate.h"
+#include <epoxy/gl.h>
+#define MAX_OLD_RATIO 0.333
+
+static void
+free_atlas (gpointer v)
+{
+ GskGLTextureAtlas *atlas = v;
+
+ gsk_gl_texture_atlas_free (atlas);
+
+ g_free (atlas);
+}
+
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_new (void)
+{
+ GskGLTextureAtlases *atlases;
+
+ atlases = g_new (GskGLTextureAtlases, 1);
+ atlases->atlases = g_ptr_array_new_with_free_func (free_atlas);
+
+ atlases->ref_count = 1;
+
+ return atlases;
+}
+
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases)
+{
+ atlases->ref_count++;
+
+ return atlases;
+}
+
+void
+gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases)
+{
+ g_assert (atlases->ref_count > 0);
+
+ if (atlases->ref_count == 1)
+ {
+ g_ptr_array_unref (atlases->atlases);
+ g_free (atlases);
+ return;
+ }
+
+ atlases->ref_count--;
+}
+
+#if 0
+static void
+write_atlas_to_png (GskGLTextureAtlas *atlas,
+ const char *filename)
+{
+ int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
+ guchar *data = g_malloc (atlas->height * stride);
+ cairo_surface_t *s;
+
+ glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
+ glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+ s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
+ cairo_surface_write_to_png (s, filename);
+
+ cairo_surface_destroy (s);
+ g_free (data);
+}
+#endif
+
+void
+gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases)
+{
+ int i;
+
+ for (i = atlases->atlases->len - 1; i >= 0; i--)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (atlases->atlases, i);
+
+ if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
+ {
+ GSK_NOTE(GLYPH_CACHE,
+ g_message ("Dropping atlas %d (%g.2%% old)", i,
+ gsk_gl_texture_atlas_get_unused_ratio (atlas)));
+
+ if (atlas->texture_id != 0)
+ {
+ glDeleteTextures (1, &atlas->texture_id);
+ atlas->texture_id = 0;
+ }
+
+ g_ptr_array_remove_index (atlases->atlases, i);
+ }
+ }
+
+#if 0
+ {
+ static guint timestamp;
+
+ timestamp++;
+ for (i = 0; i < atlases->atlases->len; i++)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (atlases->atlases, i);
+
+ if (atlas->texture_id)
+ {
+ char *filename;
+
+ filename = g_strdup_printf ("textureatlas%d-%u.png", i, timestamp);
+ write_atlas_to_png (atlas, filename);
+ g_free (filename);
+ }
+ }
+ }
+#endif
+}
void
gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
@@ -20,11 +137,19 @@ gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
width, height,
self->nodes,
width);
+
+ gsk_gl_texture_atlas_realize (self);
}
void
gsk_gl_texture_atlas_free (GskGLTextureAtlas *self)
{
+ if (self->texture_id != 0)
+ {
+ glDeleteTextures (1, &self->texture_id);
+ self->texture_id = 0;
+ }
+
g_clear_pointer (&self->nodes, g_free);
}
@@ -82,3 +207,43 @@ gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
return 0.0;
}
+/* Not using gdk_gl_driver_create_texture here, since we want
+ * this texture to survive the driver and stay around until
+ * the display gets closed.
+ */
+static guint
+create_shared_texture (int width,
+ int height)
+{
+ guint texture_id;
+
+ glGenTextures (1, &texture_id);
+ glBindTexture (GL_TEXTURE_2D, texture_id);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ else
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glBindTexture (GL_TEXTURE_2D, 0);
+
+ return texture_id;
+}
+
+void
+gsk_gl_texture_atlas_realize (GskGLTextureAtlas *atlas)
+{
+ if (atlas->texture_id)
+ return;
+
+ atlas->texture_id = create_shared_texture (atlas->width, atlas->height);
+ gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
+ GL_TEXTURE, atlas->texture_id,
+ "Glyph atlas %d", atlas->texture_id);
+}
diff --git a/gsk/gl/gskgltextureatlasprivate.h b/gsk/gl/gskgltextureatlasprivate.h
index cfa865446d..e924b32275 100644
--- a/gsk/gl/gskgltextureatlasprivate.h
+++ b/gsk/gl/gskgltextureatlasprivate.h
@@ -23,12 +23,29 @@ struct _GskGLTextureAtlas
};
typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
+struct _GskGLTextureAtlases
+{
+ int ref_count;
+
+ GPtrArray *atlases;
+};
+typedef struct _GskGLTextureAtlases GskGLTextureAtlases;
+
+GskGLTextureAtlases *gsk_gl_texture_atlases_new (void);
+GskGLTextureAtlases *gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases);
+void gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases);
+
+void gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases);
+
+
void gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
int width,
int height);
void gsk_gl_texture_atlas_free (GskGLTextureAtlas *self);
+void gsk_gl_texture_atlas_realize (GskGLTextureAtlas *self);
+
void gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
int width,
int height);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]