[gtk/glyphy2: 18/34] gsk/gl: make texture libraries more autonomous




commit 6a052d426aadf11a7c295c4ca282bc40087f3dc6
Author: Christian Hergert <chergert redhat com>
Date:   Fri Mar 18 14:51:41 2022 -0700

    gsk/gl: make texture libraries more autonomous
    
    This moves a lot of the texture atlas control out of the driver and into
    the various texture libraries through their base GskGLTextureLibrary class.
    
    Additionally, this gives more control to libraries on allocating which can
    be necessary for some tooling such as future Glyphy integration.
    
    As part of this, the 1x1 pixel initialization is moved to the Glyph library
    which is the only place where it is actually needed.
    
    The compact vfunc now is responsible for compaction and it allows for us
    to iterate the atlas hashtable a single time instead of twice as we were
    doing previously.
    
    The init_atlas vfunc is used to do per-library initialization such as
    adding a 1x1 pixel in the Glyph cache used for coloring lines.
    
    The allocate vfunc purely allocates but does no upload. This can be useful
    for situations where a library wants to reuse the allocator from the
    base class but does not want to actually insert a key/value entry. The
    glyph library uses this for it's 1x1 pixel.
    
    In the future, we will also likely want to decouple the rectangle packing
    implementation from the atlas structure, or at least move it into a union
    so that we do not allocate unused memory for alternate allocators.

 gsk/gl/gskgldriver.c                | 111 ++-------
 gsk/gl/gskgldriverprivate.h         |   5 -
 gsk/gl/gskglglyphlibrary.c          |  50 ++++
 gsk/gl/gskgltexturelibrary.c        | 466 +++++++++++++++++++++++-------------
 gsk/gl/gskgltexturelibraryprivate.h |  64 +++--
 5 files changed, 407 insertions(+), 289 deletions(-)
---
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 0980eba0ae..68311b7238 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -44,8 +44,6 @@
 #include <gdk/gdkprofilerprivate.h>
 #include <gdk/gdktextureprivate.h>
 
-#define MAX_OLD_RATIO 0.5
-
 G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
 
 static guint
@@ -155,54 +153,6 @@ gsk_gl_driver_collect_unused_textures (GskGLDriver *self,
   return collected;
 }
 
-static void
-gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
-{
-  if (atlas->texture_id != 0)
-    {
-      glDeleteTextures (1, &atlas->texture_id);
-      atlas->texture_id = 0;
-    }
-
-  g_clear_pointer (&atlas->nodes, g_free);
-  g_slice_free (GskGLTextureAtlas, atlas);
-}
-
-GskGLTextureAtlas *
-gsk_gl_driver_create_atlas (GskGLDriver *self,
-                            guint        width,
-                            guint        height)
-{
-  GskGLTextureAtlas *atlas;
-
-  g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
-  g_return_val_if_fail (width > 0, NULL);
-  g_return_val_if_fail (height > 0, NULL);
-
-  atlas = g_slice_new0 (GskGLTextureAtlas);
-  atlas->width = width;
-  atlas->height = height;
-  /* TODO: We might want to change the strategy about the amount of
-   *       nodes here? stb_rect_pack.h says width is optimal. */
-  atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
-  stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
-  atlas->texture_id = gsk_gl_command_queue_create_texture (self->command_queue,
-                                                           atlas->width,
-                                                           atlas->height,
-                                                           GL_RGBA8,
-                                                           GL_LINEAR,
-                                                           GL_LINEAR);
-
-  gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
-                                      GL_TEXTURE, atlas->texture_id,
-                                      "Texture atlas %d",
-                                      atlas->texture_id);
-
-  g_ptr_array_add (self->atlases, atlas);
-
-  return atlas;
-}
-
 static void
 remove_program (gpointer data)
 {
@@ -327,7 +277,6 @@ gsk_gl_driver_dispose (GObject *object)
   g_clear_object (&self->icons);
   g_clear_object (&self->shadows);
 
-  g_clear_pointer (&self->atlases, g_ptr_array_unref);
   g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
   g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
   g_clear_pointer (&self->textures, g_hash_table_unref);
@@ -364,7 +313,6 @@ gsk_gl_driver_init (GskGLDriver *self)
   self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, remove_program);
   self->texture_pool = g_array_new (FALSE, FALSE, sizeof (guint));
   self->render_targets = g_ptr_array_new ();
-  self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
 }
 
 static gboolean
@@ -575,37 +523,6 @@ failure:
   return g_steal_pointer (&driver);
 }
 
-static GPtrArray *
-gsk_gl_driver_compact_atlases (GskGLDriver *self)
-{
-  GPtrArray *removed = NULL;
-
-  g_assert (GSK_IS_GL_DRIVER (self));
-
-  for (guint i = self->atlases->len; i > 0; i--)
-    {
-      GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
-
-      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,
-                               100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
-          if (removed == NULL)
-            removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
-          g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
-        }
-    }
-
-  GSK_NOTE (GLYPH_CACHE, {
-    static guint timestamp;
-    if (timestamp++ % 60 == 0)
-      g_message ("%d atlases", self->atlases->len);
-  });
-
-  return removed;
-}
-
 /**
  * gsk_gl_driver_begin_frame:
  * @self: a `GskGLDriver`
@@ -622,7 +539,6 @@ gsk_gl_driver_begin_frame (GskGLDriver       *self,
                            GskGLCommandQueue *command_queue)
 {
   gint64 last_frame_id;
-  GPtrArray *removed;
 
   g_return_if_fail (GSK_IS_GL_DRIVER (self));
   g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue));
@@ -637,16 +553,11 @@ gsk_gl_driver_begin_frame (GskGLDriver       *self,
 
   gsk_gl_command_queue_begin_frame (self->command_queue);
 
-  /* Compact atlases with too many freed pixels */
-  removed = gsk_gl_driver_compact_atlases (self);
-
   /* Mark unused pixel regions of the atlases */
   gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons),
-                                      self->current_frame_id,
-                                      removed);
+                                      self->current_frame_id);
   gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
-                                      self->current_frame_id,
-                                      removed);
+                                      self->current_frame_id);
 
   /* Cleanup old shadows */
   gsk_gl_shadow_library_begin_frame (self->shadows);
@@ -657,9 +568,6 @@ gsk_gl_driver_begin_frame (GskGLDriver       *self,
    * we block on any resources while delivering our frames.
    */
   gsk_gl_driver_collect_unused_textures (self, last_frame_id - 1);
-
-  /* Now free atlas textures */
-  g_clear_pointer (&removed, g_ptr_array_unref);
 }
 
 /**
@@ -1239,14 +1147,23 @@ void
 gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
                                    const char  *directory)
 {
+  GPtrArray *atlases;
+
   g_return_if_fail (GSK_IS_GL_DRIVER (self));
 
   if (directory == NULL)
     directory = ".";
 
-  for (guint i = 0; i < self->atlases->len; i++)
+#define copy_atlases(dst, library) \
+  g_ptr_array_extend(dst, GSK_GL_TEXTURE_LIBRARY(library)->atlases, NULL, NULL)
+  atlases = g_ptr_array_new ();
+  copy_atlases (atlases, self->glyphs_library);
+  copy_atlases (atlases, self->icons);
+#undef copy_atlases
+
+  for (guint i = 0; i < atlases->len; i++)
     {
-      GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
+      GskGLTextureAtlas *atlas = g_ptr_array_index (atlases, i);
       char *filename = g_strdup_printf ("%s%sframe-%d-atlas-%d.png",
                                         directory,
                                         G_DIR_SEPARATOR_S,
@@ -1255,6 +1172,8 @@ gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
       write_atlas_to_png (self, atlas, filename);
       g_free (filename);
     }
+
+  g_ptr_array_unref (atlases);
 }
 #endif
 
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index b1c100f6f7..863240fc3e 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -109,8 +109,6 @@ struct _GskGLDriver
   GHashTable *key_to_texture_id;
   GHashTable *texture_id_to_key;
 
-  GPtrArray *atlases;
-
   GHashTable *shader_cache;
 
   GArray *autorelease_framebuffers;
@@ -184,9 +182,6 @@ void                gsk_gl_driver_add_texture_slices     (GskGLDriver         *s
 GskGLProgram      * gsk_gl_driver_lookup_shader          (GskGLDriver         *self,
                                                           GskGLShader         *shader,
                                                           GError             **error);
-GskGLTextureAtlas * gsk_gl_driver_create_atlas           (GskGLDriver         *self,
-                                                          guint                width,
-                                                          guint                height);
 
 #ifdef G_ENABLE_DEBUG
 void                gsk_gl_driver_save_atlases_to_png    (GskGLDriver         *self,
diff --git a/gsk/gl/gskglglyphlibrary.c b/gsk/gl/gskglglyphlibrary.c
index 06fe76c097..c9c42357bd 100644
--- a/gsk/gl/gskglglyphlibrary.c
+++ b/gsk/gl/gskglglyphlibrary.c
@@ -93,6 +93,55 @@ gsk_gl_glyph_library_clear_cache (GskGLTextureLibrary *library)
   memset (self->front, 0, sizeof self->front);
 }
 
+static void
+gsk_gl_glyph_library_init_atlas (GskGLTextureLibrary *self,
+                                 GskGLTextureAtlas   *atlas)
+{
+  gboolean packed G_GNUC_UNUSED;
+  int x, y;
+  guint gl_format;
+  guint gl_type;
+  guint8 pixel_data[4 * 3 * 3];
+
+  g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+  g_assert (atlas != NULL);
+
+  /* Insert a single pixel at 0,0 for use in coloring */
+
+  gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
+                                          "Initializing Atlas");
+
+  packed = gsk_gl_texture_library_allocate (self, atlas, 3, 3, &x, &y);
+  g_assert (packed);
+  g_assert (x == 0 && y == 0);
+
+  memset (pixel_data, 255, sizeof pixel_data);
+
+  if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
+    {
+      gl_format = GL_RGBA;
+      gl_type = GL_UNSIGNED_BYTE;
+    }
+  else
+    {
+      gl_format = GL_BGRA;
+      gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+    }
+
+  glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
+
+  glTexSubImage2D (GL_TEXTURE_2D, 0,
+                   0, 0,
+                   3, 3,
+                   gl_format, gl_type,
+                   pixel_data);
+
+  gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
+
+  self->driver->command_queue->n_uploads++;
+}
+
+
 static void
 gsk_gl_glyph_library_finalize (GObject *object)
 {
@@ -112,6 +161,7 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
   object_class->finalize = gsk_gl_glyph_library_finalize;
 
   library_class->clear_cache = gsk_gl_glyph_library_clear_cache;
+  library_class->init_atlas = gsk_gl_glyph_library_init_atlas;
 }
 
 static void
diff --git a/gsk/gl/gskgltexturelibrary.c b/gsk/gl/gskgltexturelibrary.c
index 649f422b0d..3e6463a848 100644
--- a/gsk/gl/gskgltexturelibrary.c
+++ b/gsk/gl/gskgltexturelibrary.c
@@ -30,6 +30,7 @@
 #define DEFAULT_MAX_FRAME_AGE 60
 #define DEFAULT_ATLAS_WIDTH 512
 #define DEFAULT_ATLAS_HEIGHT 512
+#define MAX_OLD_RATIO 0.5
 
 G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
 
@@ -41,6 +42,143 @@ enum {
 
 static GParamSpec *properties [N_PROPS];
 
+static void
+gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
+{
+  if (atlas->texture_id != 0)
+    {
+      glDeleteTextures (1, &atlas->texture_id);
+      atlas->texture_id = 0;
+    }
+
+  g_clear_pointer (&atlas->nodes, g_free);
+  g_slice_free (GskGLTextureAtlas, atlas);
+}
+
+static gboolean
+gsk_gl_texture_library_real_compact (GskGLTextureLibrary *self,
+                                     gint64               frame_id)
+{
+  GPtrArray *removed = NULL;
+  gboolean ret = FALSE;
+  gboolean periodic_scan;
+
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+  periodic_scan = (self->max_frame_age > 0 &&
+                   (frame_id % self->max_frame_age) == 0);
+
+  for (guint i = self->atlases->len; i > 0; i--)
+    {
+      GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
+
+      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,
+                               100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
+          if (removed == NULL)
+            removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
+          g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
+        }
+    }
+
+  if (periodic_scan || removed != NULL)
+    {
+      GskGLTextureAtlasEntry *entry;
+      GHashTableIter iter;
+      guint dropped = 0;
+      guint atlased = 0;
+
+      g_hash_table_iter_init (&iter, self->hash_table);
+      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
+        {
+          if (entry->is_atlased)
+            {
+              if (removed && g_ptr_array_find (removed, entry->atlas, NULL))
+                {
+                  g_hash_table_iter_remove (&iter);
+                  dropped++;
+                }
+              else if (periodic_scan)
+                {
+                  gsk_gl_texture_atlas_entry_mark_unused (entry);
+                  entry->accessed = FALSE;
+                  if (entry->is_atlased)
+                    atlased++;
+                }
+            }
+          else if (!entry->accessed)
+            {
+              gsk_gl_driver_release_texture (self->driver, entry->texture);
+              g_hash_table_iter_remove (&iter);
+              dropped++;
+            }
+        }
+
+      GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
+                                        G_OBJECT_TYPE_NAME (self),
+                                        dropped);
+                             g_message ("%s: %d items cached (%d atlased, %d individually)",
+                                        G_OBJECT_TYPE_NAME (self),
+                                        g_hash_table_size (self->hash_table),
+                                        atlased,
+                                        g_hash_table_size (self->hash_table) - atlased));
+
+      if (dropped > 0)
+        gsk_gl_texture_library_clear_cache (self);
+
+      ret = TRUE;
+
+      g_clear_pointer (&removed, g_ptr_array_unref);
+    }
+
+  GSK_NOTE (GLYPH_CACHE, {
+    static gint64 last_message;
+    gint64 now = g_get_monotonic_time ();
+    if (now - last_message > G_USEC_PER_SEC)
+      {
+        last_message = now;
+        g_message ("%s contains %d atlases",
+                   G_OBJECT_TYPE_NAME (self),
+                   self->atlases->len);
+      }
+  });
+
+  return ret;
+}
+
+static gboolean
+gsk_gl_texture_library_real_allocate (GskGLTextureLibrary *self,
+                                      GskGLTextureAtlas   *atlas,
+                                      int                  width,
+                                      int                  height,
+                                      int                 *out_x,
+                                      int                 *out_y)
+{
+  stbrp_rect rect;
+
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+  g_assert (atlas != NULL);
+  g_assert (width > 0);
+  g_assert (height > 0);
+  g_assert (out_x != NULL);
+  g_assert (out_y != NULL);
+
+  rect.w = width;
+  rect.h = height;
+
+  stbrp_pack_rects (&atlas->context, &rect, 1);
+
+  if (rect.was_packed)
+    {
+      *out_x = rect.x;
+      *out_y = rect.y;
+    }
+
+  return rect.was_packed;
+}
+
 static void
 gsk_gl_texture_library_constructed (GObject *object)
 {
@@ -108,6 +246,9 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
   object_class->get_property = gsk_gl_texture_library_get_property;
   object_class->set_property = gsk_gl_texture_library_set_property;
 
+  klass->compact = gsk_gl_texture_library_real_compact;
+  klass->allocate = gsk_gl_texture_library_real_allocate;
+
   properties [PROP_DRIVER] =
     g_param_spec_object ("driver",
                          "Driver",
@@ -124,7 +265,7 @@ gsk_gl_texture_library_init (GskGLTextureLibrary *self)
   self->max_frame_age = DEFAULT_MAX_FRAME_AGE;
   self->atlas_width = DEFAULT_ATLAS_WIDTH;
   self->atlas_height = DEFAULT_ATLAS_HEIGHT;
-  self->atlases = g_ptr_array_new ();
+  self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
 }
 
 void
@@ -143,90 +284,14 @@ gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
 
 void
 gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
-                                    gint64               frame_id,
-                                    GPtrArray            *removed_atlases)
+                                    gint64               frame_id)
 {
-  GHashTableIter iter;
-  gboolean drop_caches = FALSE;
-
   g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
 
-  if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
-    GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id, removed_atlases);
-
-  if (removed_atlases != NULL)
-    {
-      GskGLTextureAtlasEntry *entry;
-      guint dropped = 0;
-
-      /* Remove cached copy of purged atlases */
-      for (guint i = 0; i < removed_atlases->len; i++)
-        g_ptr_array_remove (self->atlases, g_ptr_array_index (removed_atlases, i));
-
-      g_hash_table_iter_init (&iter, self->hash_table);
-      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
-        {
-          if (entry->is_atlased)
-            {
-              for (guint i = 0; i < removed_atlases->len; i++)
-                {
-                  GskGLTextureAtlas *atlas = g_ptr_array_index (removed_atlases, i);
-
-                  if (atlas == entry->atlas)
-                    {
-                      g_hash_table_iter_remove (&iter);
-                      dropped++;
-                      break;
-                    }
-                }
-            }
-        }
-
-      GSK_NOTE (GLYPH_CACHE,
-                if (dropped > 0)
-                  g_message ("%s: Dropped %d items",
-                             G_OBJECT_TYPE_NAME (self), dropped));
-
-      drop_caches |= dropped > 0;
-    }
-
-  if (frame_id % self->max_frame_age == 0)
-    {
-      GskGLTextureAtlasEntry *entry;
-      int atlased = 0;
-      int dropped = 0;
-
-      g_hash_table_iter_init (&iter, self->hash_table);
-      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
-        {
-          if (!entry->is_atlased && !entry->accessed)
-            {
-              gsk_gl_driver_release_texture (self->driver, entry->texture);
-              g_hash_table_iter_remove (&iter);
-              dropped++;
-              continue;
-            }
-
-          gsk_gl_texture_atlas_entry_mark_unused (entry);
-          entry->accessed = FALSE;
-          if (entry->is_atlased)
-            atlased++;
-        }
-
-      GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
-                                        G_OBJECT_TYPE_NAME (self),
-                                        dropped);
-                             g_message ("%s: %d items cached (%d atlased, %d individually)",
-                                        G_OBJECT_TYPE_NAME (self),
-                                        g_hash_table_size (self->hash_table),
-                                        atlased,
-                                        g_hash_table_size (self->hash_table) - atlased));
-
-      drop_caches |= dropped > 0;
-    }
+  gsk_gl_texture_library_compact (self, frame_id);
 
-  if (drop_caches && GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
-    GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
+  if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
+    GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id);
 }
 
 static GskGLTexture *
@@ -253,92 +318,29 @@ gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
   return texture;
 }
 
-static inline gboolean
-gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
-                           int                width,
-                           int                height,
-                           int               *out_x,
-                           int               *out_y)
-{
-  stbrp_rect rect;
-
-  rect.w = width;
-  rect.h = height;
-
-  stbrp_pack_rects (&self->context, &rect, 1);
-
-  if (rect.was_packed)
-    {
-      *out_x = rect.x;
-      *out_y = rect.y;
-    }
-
-  return rect.was_packed;
-}
-
-static void
-gsk_gl_texture_library_init_atlas (GskGLTextureLibrary *self,
-                                   GskGLTextureAtlas   *atlas)
-{
-  /* Insert a single pixel at 0,0 for use in coloring */
-
-  gboolean packed G_GNUC_UNUSED;
-  int x, y;
-  guint gl_format;
-  guint gl_type;
-  guint8 pixel_data[4 * 3 * 3];
-
-  g_ptr_array_add (self->atlases, atlas);
-
-  gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
-                                          "Initializing Atlas");
-
-  packed = gsk_gl_texture_atlas_pack (atlas, 3, 3, &x, &y);
-  g_assert (packed);
-  g_assert (x == 0 && y == 0);
-
-  memset (pixel_data, 255, sizeof pixel_data);
-
-  if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
-    {
-      gl_format = GL_RGBA;
-      gl_type = GL_UNSIGNED_BYTE;
-    }
-  else
-    {
-      gl_format = GL_BGRA;
-      gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
-    }
-
-  glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
-
-  glTexSubImage2D (GL_TEXTURE_2D, 0,
-                   0, 0,
-                   3, 3,
-                   gl_format, gl_type,
-                   pixel_data);
-
-  gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
-
-  self->driver->command_queue->n_uploads++;
-}
-
 static void
-gsk_gl_texture_atlases_pack (GskGLTextureLibrary *self,
-                             int                  width,
-                             int                  height,
-                             GskGLTextureAtlas  **out_atlas,
-                             int                 *out_x,
-                             int                 *out_y)
+gsk_gl_texture_library_pack_any_atlas (GskGLTextureLibrary  *self,
+                                       int                   width,
+                                       int                   height,
+                                       GskGLTextureAtlas   **out_atlas,
+                                       int                  *out_x,
+                                       int                  *out_y)
 {
   GskGLTextureAtlas *atlas = NULL;
   int x, y;
 
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+  g_assert (width > 0);
+  g_assert (height > 0);
+  g_assert (out_atlas != NULL);
+  g_assert (out_x != NULL);
+  g_assert (out_y != NULL);
+
   for (guint i = 0; i < self->atlases->len; i++)
     {
       atlas = g_ptr_array_index (self->atlases, i);
 
-      if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+      if (gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
         break;
 
       atlas = NULL;
@@ -347,11 +349,10 @@ gsk_gl_texture_atlases_pack (GskGLTextureLibrary *self,
   if (atlas == NULL)
     {
       /* No atlas has enough space, so create a new one... */
-      atlas = gsk_gl_driver_create_atlas (self->driver, self->atlas_width, self->atlas_height);
-      gsk_gl_texture_library_init_atlas (self, atlas);
+      atlas = gsk_gl_texture_library_acquire_atlas (self);
 
       /* Pack it onto that one, which surely has enough space... */
-      if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+      if (!gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
         g_assert_not_reached ();
     }
 
@@ -407,12 +408,12 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
       int packed_x;
       int packed_y;
 
-      gsk_gl_texture_atlases_pack (self,
-                                   padding + width + padding,
-                                   padding + height + padding,
-                                   &atlas,
-                                   &packed_x,
-                                   &packed_y);
+      gsk_gl_texture_library_pack_any_atlas (self,
+                                             padding + width + padding,
+                                             padding + height + padding,
+                                             &atlas,
+                                             &packed_x,
+                                             &packed_y);
 
       entry->atlas = atlas;
       entry->is_atlased = TRUE;
@@ -446,3 +447,134 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
 
   return entry;
 }
+
+/*
+ * gsk_gl_texture_library_clear_cache:
+ *
+ * Clear the front cache if the texture library is using one. For
+ * example the glyph cache would drop it's front cache to force
+ * next lookups to fall through to the GHashTable key lookup.
+ */
+void
+gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self)
+{
+  g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+  if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
+    GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
+}
+
+/*
+ * gsk_gl_texture_library_compact:
+ *
+ * Requests that the texture library compact it's altases. That
+ * generally means to traverse them to look for unused pixels over
+ * a certain threshold and release them if necessary.
+ *
+ * Returns: %TRUE if any compaction occurred.
+ */
+gboolean
+gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
+                                gint64               frame_id)
+{
+  g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), FALSE);
+
+  if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact)
+    return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact (self, frame_id);
+
+  return FALSE;
+}
+
+void
+gsk_gl_texture_library_reset (GskGLTextureLibrary *self)
+{
+  g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+  gsk_gl_texture_library_clear_cache (self);
+
+  g_hash_table_remove_all (self->hash_table);
+
+  if (self->atlases->len)
+    g_ptr_array_remove_range (self->atlases, 0, self->atlases->len);
+}
+
+void
+gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
+                                       int                  width,
+                                       int                  height)
+{
+  g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+  if (width <= 0)
+    width = DEFAULT_ATLAS_WIDTH;
+
+  if (height <= 0)
+    height = DEFAULT_ATLAS_HEIGHT;
+
+  self->atlas_height = height;
+  self->atlas_width = width;
+
+  gsk_gl_texture_library_reset (self);
+}
+
+/*
+ * gsk_gl_texture_library_acquire_atlas:
+ *
+ * Allocates a new texture atlas based on the current size
+ * and format requirements.
+ */
+GskGLTextureAtlas *
+gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self)
+{
+  GskGLTextureAtlas *atlas;
+
+  g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
+  g_return_val_if_fail (GSK_IS_GL_DRIVER (self->driver), NULL);
+  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->driver->command_queue), NULL);
+  g_return_val_if_fail (self->atlas_width > 0, NULL);
+  g_return_val_if_fail (self->atlas_height > 0, NULL);
+
+  atlas = g_slice_new0 (GskGLTextureAtlas);
+  atlas->width = self->atlas_width;
+  atlas->height = self->atlas_height;
+  /* TODO: We might want to change the strategy about the amount of
+   *       nodes here? stb_rect_pack.h says width is optimal. */
+  atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
+  stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
+  atlas->texture_id = gsk_gl_command_queue_create_texture (self->driver->command_queue,
+                                                           atlas->width,
+                                                           atlas->height,
+                                                           GL_RGBA8,
+                                                           GL_LINEAR,
+                                                           GL_LINEAR);
+
+  gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
+                                      GL_TEXTURE, atlas->texture_id,
+                                      "Texture atlas %d",
+                                      atlas->texture_id);
+
+  g_ptr_array_add (self->atlases, atlas);
+
+  if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas)
+    GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas (self, atlas);
+
+  return atlas;
+}
+
+gboolean
+gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
+                                 GskGLTextureAtlas   *atlas,
+                                 int                  width,
+                                 int                  height,
+                                 int                 *out_x,
+                                 int                 *out_y)
+{
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+  g_assert (atlas != NULL);
+  g_assert (width > 0);
+  g_assert (height > 0);
+  g_assert (out_x != NULL);
+  g_assert (out_y != NULL);
+
+  return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->allocate (self, atlas, width, height, out_x, out_y);
+}
diff --git a/gsk/gl/gskgltexturelibraryprivate.h b/gsk/gl/gskgltexturelibraryprivate.h
index 167b00923b..a0292c497b 100644
--- a/gsk/gl/gskgltexturelibraryprivate.h
+++ b/gsk/gl/gskgltexturelibraryprivate.h
@@ -102,31 +102,53 @@ typedef struct _GskGLTextureLibraryClass
 {
   GObjectClass parent_class;
 
-  void (*begin_frame) (GskGLTextureLibrary *library,
-                       gint64               frame_id,
-                       GPtrArray           *removed_atlases);
-  void (*clear_cache) (GskGLTextureLibrary *library);
+  void     (*begin_frame) (GskGLTextureLibrary *library,
+                           gint64               frame_id);
+  gboolean (*compact)     (GskGLTextureLibrary *library,
+                           gint64               frame_id);
+  void     (*clear_cache) (GskGLTextureLibrary *library);
+  void     (*init_atlas)  (GskGLTextureLibrary *library,
+                           GskGLTextureAtlas   *atlas);
+  gboolean (*allocate)    (GskGLTextureLibrary *library,
+                           GskGLTextureAtlas   *atlas,
+                           int                  width,
+                           int                  height,
+                           int                 *out_x,
+                           int                 *out_y);
 } GskGLTextureLibraryClass;
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskGLTextureLibrary, g_object_unref)
 
-GType    gsk_gl_texture_library_get_type    (void) G_GNUC_CONST;
-void     gsk_gl_texture_library_set_funcs   (GskGLTextureLibrary *self,
-                                             GHashFunc            hash_func,
-                                             GEqualFunc           equal_func,
-                                             GDestroyNotify       key_destroy,
-                                             GDestroyNotify       value_destroy);
-void     gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
-                                             gint64               frame_id,
-                                             GPtrArray           *removed_atlases);
-gpointer gsk_gl_texture_library_pack        (GskGLTextureLibrary *self,
-                                             gpointer             key,
-                                             gsize                valuelen,
-                                             guint                width,
-                                             guint                height,
-                                             int                  padding,
-                                             guint               *out_packed_x,
-                                             guint               *out_packed_y);
+GType              gsk_gl_texture_library_get_type       (void) G_GNUC_CONST;
+gboolean           gsk_gl_texture_library_compact        (GskGLTextureLibrary *self,
+                                                          gint64               frame_id);
+void               gsk_gl_texture_library_clear_cache    (GskGLTextureLibrary *self);
+void               gsk_gl_texture_library_reset          (GskGLTextureLibrary *self);
+void               gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
+                                                          int                  width,
+                                                          int                  height);
+GskGLTextureAtlas *gsk_gl_texture_library_acquire_atlas  (GskGLTextureLibrary *self);
+void               gsk_gl_texture_library_set_funcs      (GskGLTextureLibrary *self,
+                                                          GHashFunc            hash_func,
+                                                          GEqualFunc           equal_func,
+                                                          GDestroyNotify       key_destroy,
+                                                          GDestroyNotify       value_destroy);
+void               gsk_gl_texture_library_begin_frame    (GskGLTextureLibrary *self,
+                                                          gint64               frame_id);
+gboolean           gsk_gl_texture_library_allocate       (GskGLTextureLibrary *self,
+                                                          GskGLTextureAtlas   *atlas,
+                                                          int                  width,
+                                                          int                  height,
+                                                          int                 *out_x,
+                                                          int                 *out_y);
+gpointer           gsk_gl_texture_library_pack           (GskGLTextureLibrary *self,
+                                                          gpointer             key,
+                                                          gsize                valuelen,
+                                                          guint                width,
+                                                          guint                height,
+                                                          int                  padding,
+                                                          guint               *out_packed_x,
+                                                          guint               *out_packed_y);
 
 static inline void
 gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]