[gtk/wip/matthiasc/shared-glyph-cache] Move towards a unified cache



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, &region))
+  if (render_glyph (key, value, &region))
     {
-      upload_region_or_else (self, atlas->texture_id, &region);
+      upload_region (value->atlas->texture_id, &region);
       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]