[gtk/wip/chergert/glproto: 265/526] start on texture atlasing plumbing from driver to libraries




commit 026f22f012e365ae0c6de0f2c362e9fcf3f1e4a0
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jan 18 21:42:42 2021 -0800

    start on texture atlasing plumbing from driver to libraries

 gsk/meson.build                       |   1 -
 gsk/next/gskgldriver.c                |  56 +++++++-
 gsk/next/gskgldriverprivate.h         |  81 +++++------
 gsk/next/gskglglyphlibrary.c          |  48 +++++--
 gsk/next/gskglglyphlibraryprivate.h   |  59 ++------
 gsk/next/gskgliconlibrary.c           |   7 +-
 gsk/next/gskgliconlibraryprivate.h    |   2 +-
 gsk/next/gskglrenderjob.c             |  25 ++--
 gsk/next/gskglshadowlibrary.c         |   7 +-
 gsk/next/gskglshadowlibraryprivate.h  |   2 +-
 gsk/next/gskgltextureatlas.c          | 252 ++++++++++++++++++++++++++++++----
 gsk/next/gskgltextureatlasprivate.h   |  56 ++++----
 gsk/next/gskgltexturelibrary.c        | 224 ++++++++++++++++++++++--------
 gsk/next/gskgltexturelibraryprivate.h | 176 ++++++++++++++++++++----
 gsk/next/gskgltypesprivate.h          |   1 +
 15 files changed, 738 insertions(+), 259 deletions(-)
---
diff --git a/gsk/meson.build b/gsk/meson.build
index 6c1f4c7566..62193a79da 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -59,7 +59,6 @@ gsk_private_sources = files([
   'next/gskglprogram.c',
   'next/gskglrenderjob.c',
   'next/gskglshadowlibrary.c',
-  'next/gskgltextureatlas.c',
   'next/gskgltexturelibrary.c',
   'next/gskgluniformstate.c',
   'next/gskgltexturepool.c',
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index 8ef7e28c5b..a0dfeba9eb 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -40,6 +40,7 @@
 #include "gskgltexturepoolprivate.h"
 
 #define TEXTURES_CACHED_FOR_N_FRAMES 1
+#define ATLAS_SIZE 512
 
 G_DEFINE_TYPE (GskNextDriver, gsk_next_driver, G_TYPE_OBJECT)
 
@@ -132,6 +133,49 @@ gsk_next_driver_collect_unused_textures (GskNextDriver *self,
   return old_size - g_hash_table_size (self->textures);
 }
 
+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_next_driver_create_atlas (GskNextDriver *self)
+{
+  GskGLTextureAtlas *atlas;
+
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), NULL);
+
+  atlas = g_slice_new0 (GskGLTextureAtlas);
+  atlas->width = ATLAS_SIZE;
+  atlas->height = ATLAS_SIZE;
+  /* 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_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
 gsk_next_driver_shader_weak_cb (gpointer  data,
                                 GObject  *where_object_was)
@@ -197,6 +241,11 @@ gsk_next_driver_dispose (GObject *object)
       self->autorelease_framebuffers->len = 0;
     }
 
+  g_clear_object (&self->glyphs);
+  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);
@@ -232,6 +281,7 @@ gsk_next_driver_init (GskNextDriver *self)
                                               g_object_unref);
   gsk_gl_texture_pool_init (&self->texture_pool);
   self->render_targets = g_ptr_array_new ();
+  self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
 }
 
 static gboolean
@@ -342,9 +392,9 @@ gsk_next_driver_new (GskGLCommandQueue  *command_queue,
       return NULL;
     }
 
-  self->glyphs = gsk_gl_glyph_library_new (context);
-  self->icons = gsk_gl_icon_library_new (context);
-  self->shadows = gsk_gl_shadow_library_new (context);
+  self->glyphs = gsk_gl_glyph_library_new (self);
+  self->icons = gsk_gl_icon_library_new (self);
+  self->shadows = gsk_gl_shadow_library_new (self);
 
   return g_steal_pointer (&self);
 }
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
index 6e843e0a93..120409d509 100644
--- a/gsk/next/gskgldriverprivate.h
+++ b/gsk/next/gskgldriverprivate.h
@@ -95,6 +95,8 @@ struct _GskNextDriver
   GHashTable *key_to_texture_id;
   GHashTable *texture_id_to_key;
 
+  GPtrArray *atlases;
+
   GHashTable *shader_cache;
 
   GArray *autorelease_framebuffers;
@@ -114,45 +116,46 @@ struct _GskNextDriver
   guint in_frame : 1;
 };
 
-GskNextDriver *gsk_next_driver_new                   (GskGLCommandQueue    *command_queue,
-                                                      gboolean              debug,
-                                                      GError              **error);
-GdkGLContext  *gsk_next_driver_get_context           (GskNextDriver        *self);
-gboolean       gsk_next_driver_create_render_target  (GskNextDriver        *self,
-                                                      int                   width,
-                                                      int                   height,
-                                                      int                   min_filter,
-                                                      int                   mag_filter,
-                                                      GskGLRenderTarget   **render_target);
-guint          gsk_next_driver_release_render_target (GskNextDriver        *self,
-                                                      GskGLRenderTarget    *render_target,
-                                                      gboolean              release_texture);
-void           gsk_next_driver_begin_frame           (GskNextDriver        *self);
-void           gsk_next_driver_end_frame             (GskNextDriver        *self);
-guint          gsk_next_driver_lookup_texture        (GskNextDriver        *self,
-                                                      const GskTextureKey  *key);
-void           gsk_next_driver_cache_texture         (GskNextDriver        *self,
-                                                      const GskTextureKey  *key,
-                                                      guint                 texture_id);
-guint          gsk_next_driver_load_texture          (GskNextDriver        *self,
-                                                      GdkTexture           *texture,
-                                                      int                   min_filter,
-                                                      int                   mag_filter);
-GskGLTexture  *gsk_next_driver_create_texture        (GskNextDriver        *self,
-                                                      float                 width,
-                                                      float                 height,
-                                                      int                   min_filter,
-                                                      int                   mag_filter);
-GskGLTexture  *gsk_next_driver_acquire_texture       (GskNextDriver        *self,
-                                                      float                 width,
-                                                      float                 height,
-                                                      int                   min_filter,
-                                                      int                   mag_filter);
-void           gsk_next_driver_release_texture       (GskNextDriver        *self,
-                                                      GskGLTexture         *texture);
-GskGLProgram  *gsk_next_driver_lookup_shader         (GskNextDriver        *self,
-                                                      GskGLShader          *shader,
-                                                      GError              **error);
+GskNextDriver     *gsk_next_driver_new                   (GskGLCommandQueue    *command_queue,
+                                                          gboolean              debug,
+                                                          GError              **error);
+GdkGLContext      *gsk_next_driver_get_context           (GskNextDriver        *self);
+gboolean           gsk_next_driver_create_render_target  (GskNextDriver        *self,
+                                                          int                   width,
+                                                          int                   height,
+                                                          int                   min_filter,
+                                                          int                   mag_filter,
+                                                          GskGLRenderTarget   **render_target);
+guint              gsk_next_driver_release_render_target (GskNextDriver        *self,
+                                                          GskGLRenderTarget    *render_target,
+                                                          gboolean              release_texture);
+void               gsk_next_driver_begin_frame           (GskNextDriver        *self);
+void               gsk_next_driver_end_frame             (GskNextDriver        *self);
+guint              gsk_next_driver_lookup_texture        (GskNextDriver        *self,
+                                                          const GskTextureKey  *key);
+void               gsk_next_driver_cache_texture         (GskNextDriver        *self,
+                                                          const GskTextureKey  *key,
+                                                          guint                 texture_id);
+guint              gsk_next_driver_load_texture          (GskNextDriver        *self,
+                                                          GdkTexture           *texture,
+                                                          int                   min_filter,
+                                                          int                   mag_filter);
+GskGLTexture      *gsk_next_driver_create_texture        (GskNextDriver        *self,
+                                                          float                 width,
+                                                          float                 height,
+                                                          int                   min_filter,
+                                                          int                   mag_filter);
+GskGLTexture      *gsk_next_driver_acquire_texture       (GskNextDriver        *self,
+                                                          float                 width,
+                                                          float                 height,
+                                                          int                   min_filter,
+                                                          int                   mag_filter);
+void               gsk_next_driver_release_texture       (GskNextDriver        *self,
+                                                          GskGLTexture         *texture);
+GskGLProgram      *gsk_next_driver_lookup_shader         (GskNextDriver        *self,
+                                                          GskGLShader          *shader,
+                                                          GError              **error);
+GskGLTextureAtlas *gsk_next_driver_create_atlas          (GskNextDriver        *self);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglglyphlibrary.c b/gsk/next/gskglglyphlibrary.c
index 858d72cfcb..3710697636 100644
--- a/gsk/next/gskglglyphlibrary.c
+++ b/gsk/next/gskglglyphlibrary.c
@@ -20,17 +20,20 @@
 
 #include "config.h"
 
+#include "gskgldriverprivate.h"
 #include "gskglglyphlibraryprivate.h"
 
+#define MAX_GLYPH_SIZE 128
+
 G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
 
 GskGLGlyphLibrary *
-gsk_gl_glyph_library_new (GdkGLContext *context)
+gsk_gl_glyph_library_new (GskNextDriver *driver)
 {
-  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
 
   return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
-                       "context", context,
+                       "driver", driver,
                        NULL);
 }
 
@@ -93,10 +96,12 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
 static void
 gsk_gl_glyph_library_init (GskGLGlyphLibrary *self)
 {
-  self->hash_table = g_hash_table_new_full (gsk_gl_glyph_key_hash,
-                                            gsk_gl_glyph_key_equal,
-                                            gsk_gl_glyph_key_free,
-                                            gsk_gl_glyph_value_free);
+  GSK_GL_TEXTURE_LIBRARY (self)->max_entry_size = MAX_GLYPH_SIZE;
+  gsk_gl_texture_library_set_funcs (GSK_GL_TEXTURE_LIBRARY (self),
+                                    gsk_gl_glyph_key_hash,
+                                    gsk_gl_glyph_key_equal,
+                                    gsk_gl_glyph_key_free,
+                                    gsk_gl_glyph_value_free);
 }
 
 gboolean
@@ -104,9 +109,36 @@ gsk_gl_glyph_library_add (GskGLGlyphLibrary      *self,
                           const GskGLGlyphKey    *key,
                           const GskGLGlyphValue **out_value)
 {
+  PangoRectangle ink_rect;
+  GskGLGlyphValue *value;
+  int width;
+  int height;
+
   g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
   g_assert (key != NULL);
   g_assert (out_value != NULL);
 
-  return FALSE;
+  pango_font_get_glyph_extents (key->font, key->glyph, &ink_rect, NULL);
+  pango_extents_to_pixels (&ink_rect, NULL);
+
+  if (key->xshift != 0)
+    ink_rect.width++;
+  if (key->yshift != 0)
+    ink_rect.height++;
+
+  width = ink_rect.width * key->scale / 1024;
+  height = ink_rect.height * key->scale / 1024;
+
+  value = gsk_gl_texture_library_pack (GSK_GL_TEXTURE_LIBRARY (self),
+                                       key,
+                                       sizeof *key,
+                                       sizeof *value,
+                                       width,
+                                       height);
+
+  memcpy (&value->ink_rect, &ink_rect, sizeof ink_rect);
+
+
+
+  return TRUE;
 }
diff --git a/gsk/next/gskglglyphlibraryprivate.h b/gsk/next/gskglglyphlibraryprivate.h
index dd17c516c3..b373339ce1 100644
--- a/gsk/next/gskglglyphlibraryprivate.h
+++ b/gsk/next/gskglglyphlibraryprivate.h
@@ -27,6 +27,8 @@
 
 G_BEGIN_DECLS
 
+#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type())
+
 typedef struct _GskGLGlyphKey
 {
   PangoFont *font;
@@ -36,33 +38,18 @@ typedef struct _GskGLGlyphKey
   guint scale  : 26; /* times 1024 */
 } GskGLGlyphKey;
 
+typedef struct _GskGLGlyphValue
+{
+  GskGLTextureAtlasEntry entry;
+  PangoRectangle ink_rect;
+} GskGLGlyphValue;
+
 #if GLIB_SIZEOF_VOID_P == 8
 G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 16);
 #elif GLIB_SIZEOF_VOID_P == 4
 G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 12);
 #endif
 
-typedef struct _GskGLGlyphValue
-{
-  GskGLTextureAtlas *atlas;
-  guint texture_id;
-
-  float tx;
-  float ty;
-  float tw;
-  float th;
-
-  int draw_x;
-  int draw_y;
-  int draw_width;
-  int draw_height;
-
-  guint accessed : 1; /* accessed since last check */
-  guint used     : 1; /* accounted as used in the atlas */
-} GskGLGlyphValue;
-
-#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type())
-
 G_DECLARE_FINAL_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK, GL_GLYPH_LIBRARY, GskGLTextureLibrary)
 
 struct _GskGLGlyphLibrary
@@ -71,7 +58,7 @@ struct _GskGLGlyphLibrary
   GHashTable *hash_table;
 };
 
-GskGLGlyphLibrary *gsk_gl_glyph_library_new (GdkGLContext           *context);
+GskGLGlyphLibrary *gsk_gl_glyph_library_new (GskNextDriver          *driver);
 gboolean           gsk_gl_glyph_library_add (GskGLGlyphLibrary      *self,
                                              const GskGLGlyphKey    *key,
                                              const GskGLGlyphValue **out_value);
@@ -98,32 +85,12 @@ gsk_gl_glyph_library_lookup_or_add (GskGLGlyphLibrary      *self,
                                     const GskGLGlyphKey    *key,
                                     const GskGLGlyphValue **out_value)
 {
-  GskGLGlyphValue *value = g_hash_table_lookup (self->hash_table, key);
-
-  /* Optimize for the fast path (repeated lookups of a character */
-  if G_LIKELY (value && value->accessed && value->used)
-    {
-      *out_value = value;
-      return value->texture_id > 0;
-    }
+  GskGLTextureAtlasEntry *entry;
 
-  /* We found it, but haven't marked as used for this frame */
-  if (value != NULL)
+  if G_LIKELY (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry))
     {
-      value->accessed = TRUE;
-
-      if (!value->used)
-        {
-          gsk_gl_texture_library_mark_used (self,
-                                            value->atlas,
-                                            value->draw_width,
-                                            value->draw_height);
-          value->used = TRUE;
-        }
-
-      *out_value = value;
-
-      return value->texture_id > 0;
+      *out_value = (GskGLGlyphValue *)entry;
+      return TRUE;
     }
 
   return gsk_gl_glyph_library_add (self, key, out_value);
diff --git a/gsk/next/gskgliconlibrary.c b/gsk/next/gskgliconlibrary.c
index 64e800d115..5cbdccb04a 100644
--- a/gsk/next/gskgliconlibrary.c
+++ b/gsk/next/gskgliconlibrary.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include "gskgldriverprivate.h"
 #include "gskgliconlibraryprivate.h"
 
 struct _GskGLIconLibrary
@@ -30,12 +31,12 @@ struct _GskGLIconLibrary
 G_DEFINE_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
 
 GskGLIconLibrary *
-gsk_gl_icon_library_new (GdkGLContext *context)
+gsk_gl_icon_library_new (GskNextDriver *driver)
 {
-  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
 
   return g_object_new (GSK_TYPE_GL_ICON_LIBRARY,
-                       "context", context,
+                       "driver", driver,
                        NULL);
 }
 
diff --git a/gsk/next/gskgliconlibraryprivate.h b/gsk/next/gskgliconlibraryprivate.h
index c6b4363e33..37812cc554 100644
--- a/gsk/next/gskgliconlibraryprivate.h
+++ b/gsk/next/gskgliconlibraryprivate.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK, GL_ICON_LIBRARY, GskGLTextureLibrary)
 
-GskGLIconLibrary *gsk_gl_icon_library_new (GdkGLContext *context);
+GskGLIconLibrary *gsk_gl_icon_library_new (GskNextDriver *driver);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 995b3c927a..9ba57d7afd 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -2524,6 +2524,7 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
       float tx, ty, tx2, ty2;
       float cx;
       float cy;
+      guint texture_id;
 
       if (gi->glyph == PANGO_GLYPH_EMPTY)
         continue;
@@ -2536,27 +2537,27 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
       if (!gsk_gl_glyph_library_lookup_or_add (library, &lookup, &glyph))
         goto next;
 
-      g_assert (glyph->texture_id != 0);
+      texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (glyph);
 
-      if (last_texture != glyph->texture_id)
+      if (last_texture != texture_id)
         {
           gsk_gl_program_set_uniform_texture (program,
                                               UNIFORM_SHARED_SOURCE,
                                               GL_TEXTURE_2D,
                                               GL_TEXTURE0,
-                                              glyph->texture_id);
-          last_texture = glyph->texture_id;
+                                              texture_id);
+          last_texture = texture_id;
         }
 
-      tx  = glyph->tx;
-      ty  = glyph->ty;
-      tx2 = tx + glyph->tw;
-      ty2 = ty + glyph->th;
+      tx = glyph->entry.area.origin.x;
+      ty = glyph->entry.area.origin.y;
+      tx2 = tx + glyph->entry.area.size.width;
+      ty2 = ty + glyph->entry.area.size.height;
 
-      glyph_x = floor (x + cx + 0.125) + glyph->draw_x;
-      glyph_y = floor (y + cy + 0.125) + glyph->draw_y;
-      glyph_x2 = glyph_x + glyph->draw_width;
-      glyph_y2 = glyph_y + glyph->draw_height;
+      glyph_x = floor (x + cx + 0.125) + glyph->ink_rect.x;
+      glyph_y = floor (y + cy + 0.125) + glyph->ink_rect.y;
+      glyph_x2 = glyph_x + glyph->ink_rect.width;
+      glyph_y2 = glyph_y + glyph->ink_rect.height;
 
       vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
 
diff --git a/gsk/next/gskglshadowlibrary.c b/gsk/next/gskglshadowlibrary.c
index af611adba7..dbcba6f6ed 100644
--- a/gsk/next/gskglshadowlibrary.c
+++ b/gsk/next/gskglshadowlibrary.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include "gskgldriverprivate.h"
 #include "gskglshadowlibraryprivate.h"
 
 struct _GskGLShadowLibrary
@@ -30,12 +31,12 @@ struct _GskGLShadowLibrary
 G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
 
 GskGLShadowLibrary *
-gsk_gl_shadow_library_new (GdkGLContext *context)
+gsk_gl_shadow_library_new (GskNextDriver *driver)
 {
-  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
 
   return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY,
-                       "context", context,
+                       "driver", driver,
                        NULL);
 }
 
diff --git a/gsk/next/gskglshadowlibraryprivate.h b/gsk/next/gskglshadowlibraryprivate.h
index 3493e6d93d..57f952395c 100644
--- a/gsk/next/gskglshadowlibraryprivate.h
+++ b/gsk/next/gskglshadowlibraryprivate.h
@@ -35,7 +35,7 @@ typedef struct _GskGLShadowKey
 
 G_DECLARE_FINAL_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK, GL_SHADOW_LIBRARY, GskGLTextureLibrary)
 
-GskGLShadowLibrary *gsk_gl_shadow_library_new (GdkGLContext *context);
+GskGLShadowLibrary *gsk_gl_shadow_library_new (GskNextDriver *driver);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskgltextureatlas.c b/gsk/next/gskgltextureatlas.c
index d0b98e89bc..1456c77fe3 100644
--- a/gsk/next/gskgltextureatlas.c
+++ b/gsk/next/gskgltextureatlas.c
@@ -1,46 +1,238 @@
-/* gskgltextureatlas.c
- *
- * Copyright 2020 Christian Hergert <chergert redhat com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
 #include "config.h"
 
+#include <epoxy/gl.h>
+
 #include "gskgltextureatlasprivate.h"
+#include "gskdebugprivate.h"
+#include "gdkglcontextprivate.h"
+
+#define ATLAS_SIZE    512
+#define MAX_OLD_RATIO 0.5
 
-struct _GskGLTextureAtlas
+void
+gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
 {
-  GObject parent_instance;
-};
+  if (atlas->texture_id != 0)
+    {
+      glDeleteTextures (1, &atlas->texture_id);
+      atlas->texture_id = 0;
+    }
 
-G_DEFINE_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, G_TYPE_OBJECT)
+  g_clear_pointer (&atlas->nodes, g_free);
+  g_slice_free (GskGLTextureAtlas, atlas);
+}
 
-GskGLTextureAtlas *
-gsk_gl_texture_atlas_new (void)
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_new (void)
 {
-  return g_object_new (GSK_TYPE_GL_TEXTURE_ATLAS, NULL);
+  return g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
 }
 
+#if 0
 static void
-gsk_gl_texture_atlas_class_init (GskGLTextureAtlasClass *klass)
+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
 
-static void
-gsk_gl_texture_atlas_init (GskGLTextureAtlas *self)
+void
+gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *self,
+                                    GPtrArray           *removed)
+{
+  g_return_if_fail (self != NULL);
+
+  for (int i = self->len - 1; i >= 0; i--)
+    {
+      GskGLTextureAtlas *atlas = g_ptr_array_index (self, i);
+
+      if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
+        {
+          if (atlas->texture_id != 0)
+            {
+              glDeleteTextures (1, &atlas->texture_id);
+              atlas->texture_id = 0;
+            }
+
+          g_ptr_array_add (removed, atlas);
+          g_ptr_array_remove_index (self, i);
+       }
+    }
+
+#if 0
+  {
+    static guint timestamp;
+
+    timestamp++;
+    if (timestamp % 10 == 0)
+      for (i = 0; i < self->len; i++)
+        {
+          GskGLTextureAtlas *atlas = g_ptr_array_index (self, 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
+}
+
+gboolean
+gsk_gl_texture_atlases_pack (GskGLTextureAtlases *self,
+                             int                  width,
+                             int                  height,
+                             GskGLTextureAtlas  **atlas_out,
+                             int                 *out_x,
+                             int                 *out_y)
+{
+  GskGLTextureAtlas *atlas;
+  int x, y;
+
+  g_assert (width  < ATLAS_SIZE);
+  g_assert (height < ATLAS_SIZE);
+
+  atlas = NULL;
+
+  for (guint i = 0; i < self->len; i++)
+    {
+      atlas = g_ptr_array_index (self, i);
+
+      if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+        break;
+
+      atlas = NULL;
+    }
+
+  if (atlas == NULL)
+    {
+      /* 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);
+      gsk_gl_texture_atlas_realize (atlas);
+      g_ptr_array_add (self, atlas);
+
+      /* Pack it onto that one, which surely has enough space... */
+      if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+        g_assert_not_reached ();
+    }
+
+  *atlas_out = atlas;
+  *out_x = x;
+  *out_y = y;
+
+  return TRUE;
+}
+
+void
+gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
+                           int                width,
+                           int                height)
 {
+  memset (self, 0, sizeof (*self));
+
+  self->texture_id = 0;
+  self->width = width;
+  self->height = height;
+
+  /* TODO: We might want to change the strategy about the amount of
+   *       nodes here? stb_rect_pack.h says with is optimal. */
+  self->nodes = g_malloc0 (sizeof (struct stbrp_node) * width);
+  stbrp_init_target (&self->context,
+                     width, height,
+                     self->nodes,
+                     width);
+
+  gsk_gl_texture_atlas_realize (self);
+}
+
+gboolean
+gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
+                           int                width,
+                           int                height,
+                           int               *out_x,
+                           int               *out_y)
+{
+  stbrp_rect rect;
+
+  g_assert (out_x);
+  g_assert (out_y);
+
+  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;
+}
+
+double
+gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
+{
+  if (self->unused_pixels > 0)
+    return (double)(self->unused_pixels) / (double)(self->width * self->height);
+
+  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,
+                                      "Texture atlas %d", atlas->texture_id);
 }
diff --git a/gsk/next/gskgltextureatlasprivate.h b/gsk/next/gskgltextureatlasprivate.h
index ef840d30d3..289363ef7f 100644
--- a/gsk/next/gskgltextureatlasprivate.h
+++ b/gsk/next/gskgltextureatlasprivate.h
@@ -1,35 +1,35 @@
-/* gskgltextureatlasprivate.h
- *
- * Copyright 2020 Christian Hergert <chergert redhat com>
- *
- * This file is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
- * any later version.
- *
- * This file is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#ifndef __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
-#define __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
-
-#include "gskgltypesprivate.h"
+#ifndef __GSK_GL_TEXTURE_ATLAS_H__
+#define __GSK_GL_TEXTURE_ATLAS_H__
 
-G_BEGIN_DECLS
+#include "../gl/stb_rect_pack.h"
+#include "gskgldriverprivate.h"
 
-#define GSK_TYPE_GL_TEXTURE_ATLAS (gsk_gl_texture_atlas_get_type())
+G_BEGIN_DECLS
 
-G_DECLARE_FINAL_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, GSK, GL_TEXTURE_ATLAS, GObject)
+typedef GPtrArray GskGLTextureAtlases;
+
+
+GskGLTextureAtlases *gsk_gl_texture_atlases_new            (void);
+void                 gsk_gl_texture_atlases_begin_frame    (GskGLTextureAtlases      *atlases,
+                                                            GPtrArray                *removed);
+gboolean             gsk_gl_texture_atlases_pack           (GskGLTextureAtlases      *atlases,
+                                                            int                       width,
+                                                            int                       height,
+                                                            GskGLTextureAtlas       **atlas_out,
+                                                            int                      *out_x,
+                                                            int                      *out_y);
+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);
+gboolean             gsk_gl_texture_atlas_pack             (GskGLTextureAtlas        *self,
+                                                            int                       width,
+                                                            int                       height,
+                                                            int                      *out_x,
+                                                            int                      *out_y);
+double               gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas  *self);
 
-GskGLTextureAtlas *gsk_gl_texture_atlas_new (void);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskgltexturelibrary.c b/gsk/next/gskgltexturelibrary.c
index 2847c48e5a..c4ef5e849d 100644
--- a/gsk/next/gskgltexturelibrary.c
+++ b/gsk/next/gskgltexturelibrary.c
@@ -20,40 +20,34 @@
 
 #include "config.h"
 
-#include "gskgltextureatlasprivate.h"
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
 #include "gskgltexturelibraryprivate.h"
 
-typedef struct
-{
-  guint  hash;
-  guint  len;
-  guint8 key[0];
-} Entry;
-
-typedef struct
-{
-  GdkGLContext *context;
-  GHashFunc hash_func;
-  GEqualFunc equal_func;
-} GskGLTextureLibraryPrivate;
-
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
 
 enum {
   PROP_0,
-  PROP_CONTEXT,
+  PROP_DRIVER,
   N_PROPS
 };
 
 static GParamSpec *properties [N_PROPS];
 
+static void
+gsk_gl_texture_library_constructed (GObject *object)
+{
+  G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->constructed (object);
+
+  g_assert (GSK_GL_TEXTURE_LIBRARY (object)->hash_table != NULL);
+}
+
 static void
 gsk_gl_texture_library_dispose (GObject *object)
 {
   GskGLTextureLibrary *self = (GskGLTextureLibrary *)object;
-  GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
 
-  g_clear_object (&priv->context);
+  g_clear_object (&self->driver);
 
   G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object);
 }
@@ -65,12 +59,11 @@ gsk_gl_texture_library_get_property (GObject    *object,
                                      GParamSpec *pspec)
 {
   GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
-  GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
 
   switch (prop_id)
     {
-    case PROP_CONTEXT:
-      g_value_set_object (value, priv->context);
+    case PROP_DRIVER:
+      g_value_set_object (value, self->driver);
       break;
 
     default:
@@ -85,12 +78,11 @@ gsk_gl_texture_library_set_property (GObject      *object,
                                      GParamSpec   *pspec)
 {
   GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
-  GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
 
   switch (prop_id)
     {
-    case PROP_CONTEXT:
-      priv->context = g_value_dup_object (value);
+    case PROP_DRIVER:
+      self->driver = g_value_dup_object (value);
       break;
 
     default:
@@ -103,15 +95,16 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+  object_class->constructed = gsk_gl_texture_library_constructed;
   object_class->dispose = gsk_gl_texture_library_dispose;
   object_class->get_property = gsk_gl_texture_library_get_property;
   object_class->set_property = gsk_gl_texture_library_set_property;
 
-  properties [PROP_CONTEXT] =
-    g_param_spec_object ("context",
-                         "Context",
-                         "Context",
-                         GDK_TYPE_GL_CONTEXT,
+  properties [PROP_DRIVER] =
+    g_param_spec_object ("driver",
+                         "Driver",
+                         "Driver",
+                         GSK_TYPE_NEXT_DRIVER,
                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
@@ -120,21 +113,20 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
 static void
 gsk_gl_texture_library_init (GskGLTextureLibrary *self)
 {
-  GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
-
-  priv->hash_func = g_direct_hash;
-  priv->equal_func = g_direct_equal;
 }
 
-GdkGLContext *
-gsk_gl_texture_library_get_context (GskGLTextureLibrary *self)
+void
+gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
+                                  GHashFunc            hash_func,
+                                  GEqualFunc           equal_func,
+                                  GDestroyNotify       key_destroy,
+                                  GDestroyNotify       value_destroy)
 {
-  GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
-
-  g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
-  g_return_val_if_fail (GDK_IS_GL_CONTEXT (priv->context), NULL);
+  g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+  g_return_if_fail (self->hash_table == NULL);
 
-  return priv->context;
+  self->hash_table = g_hash_table_new_full (hash_func, equal_func,
+                                            key_destroy, value_destroy);
 }
 
 void
@@ -155,25 +147,145 @@ gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self)
     GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame (self);
 }
 
-gboolean
-gsk_gl_texture_library_pack (GskGLTextureLibrary  *self,
-                             gconstpointer         key,
-                             gsize                 keylen,
-                             float                 width,
-                             float                 height,
-                             GskGLTextureAtlas   **atlas,
-                             graphene_rect_t      *area)
+static GskGLTexture *
+gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
+                                 guint                width,
+                                 guint                height)
 {
-  return FALSE;
+  GskGLTexture *texture;
+
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+  if (width > self->driver->command_queue->max_texture_size ||
+      height > self->driver->command_queue->max_texture_size)
+    {
+      g_warning ("Clipping requested texture of size %ux%u to maximum allowable size %u.",
+                 width, height, self->driver->command_queue->max_texture_size);
+      width = MIN (width, self->driver->command_queue->max_texture_size);
+      height = MIN (height, self->driver->command_queue->max_texture_size);
+    }
+
+  texture = gsk_next_driver_create_texture (self->driver, width, height, GL_LINEAR, GL_LINEAR);
+  texture->permanent = TRUE;
+
+  return texture;
 }
 
-gboolean
-gsk_gl_texture_library_lookup (GskGLTextureLibrary  *library,
-                               gconstpointer         key,
-                               GskGLTextureAtlas   **atlas,
-                               graphene_rect_t      *area)
+static inline gboolean
+gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
+                           int                width,
+                           int                height,
+                           int               *out_x,
+                           int               *out_y)
 {
-  return FALSE;
+  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_atlases_pack (GskNextDriver      *driver,
+                             int                 width,
+                             int                 height,
+                             GskGLTextureAtlas **out_atlas,
+                             int                *out_x,
+                             int                *out_y)
+{
+  GskGLTextureAtlas *atlas = NULL;
+  int x, y;
+
+  for (guint i = 0; i < driver->atlases->len; i++)
+    {
+      atlas = g_ptr_array_index (driver->atlases, i);
+
+      if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+        break;
+
+      atlas = NULL;
+    }
+
+  if (atlas == NULL)
+    {
+      /* No atlas has enough space, so create a new one... */
+      atlas = gsk_next_driver_create_atlas (driver);
+
+      /* Pack it onto that one, which surely has enough space... */
+      if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
+        g_assert_not_reached ();
+    }
+
+  *out_atlas = atlas;
+  *out_x = x;
+  *out_y = y;
+}
+
+gpointer
+gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
+                             gconstpointer        key,
+                             gsize                keylen,
+                             gsize                valuelen,
+                             guint                width,
+                             guint                height)
+{
+  GskGLTextureAtlasEntry *entry;
+  GskGLTextureAtlas *atlas = NULL;
+
+  g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
+  g_assert (key != NULL);
+  g_assert (keylen > 0);
+  g_assert (valuelen > sizeof (GskGLTextureAtlasEntry));
+
+  entry = g_slice_alloc0 (valuelen);
+  entry->n_pixels = width * height;
+  entry->accessed = TRUE;
+
+  if (width <= self->max_entry_size && height <= self->max_entry_size)
+    {
+      int packed_x;
+      int packed_y;
+
+      gsk_gl_texture_atlases_pack (self->driver,
+                                   width + 2,
+                                   height + 2,
+                                   &atlas,
+                                   &packed_x,
+                                   &packed_y);
+
+      entry->atlas = atlas;
+      entry->is_atlased = TRUE;
+      entry->area.origin.x = (float)(packed_x + 1) / atlas->width;
+      entry->area.origin.y = (float)(packed_y + 1) / atlas->height;
+      entry->area.size.width = width / atlas->width;
+      entry->area.size.height = height / atlas->height;
+    }
+  else
+    {
+      GskGLTexture *texture = gsk_gl_texture_library_pack_one (self, width + 2, height + 2);
+
+      entry->texture = texture;
+      entry->is_atlased = FALSE;
+      entry->area.origin.x = 0.0f;
+      entry->area.origin.y = 0.0f;
+      entry->area.size.width = 1.0f;
+      entry->area.size.height = 1.0f;
+    }
+
+  g_hash_table_insert (self->hash_table,
+                       g_slice_copy (keylen, key),
+                       entry);
+
+  return entry;
 }
 
 void
diff --git a/gsk/next/gskgltexturelibraryprivate.h b/gsk/next/gskgltexturelibraryprivate.h
index 36e7a8d506..491f87b6b8 100644
--- a/gsk/next/gskgltexturelibraryprivate.h
+++ b/gsk/next/gskgltexturelibraryprivate.h
@@ -22,46 +22,166 @@
 #define __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__
 
 #include "gskgltypesprivate.h"
+#include "gskgltexturepoolprivate.h"
+#include "../gl/stb_rect_pack.h"
 
 G_BEGIN_DECLS
 
-#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_gl_texture_library_get_type())
+#define GSK_TYPE_GL_TEXTURE_LIBRARY            (gsk_gl_texture_library_get_type ())
+#define GSK_GL_TEXTURE_LIBRARY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibrary))
+#define GSK_IS_GL_TEXTURE_LIBRARY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GSK_TYPE_GL_TEXTURE_LIBRARY))
+#define GSK_GL_TEXTURE_LIBRARY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass))
+#define GSK_IS_GL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GSK_TYPE_GL_TEXTURE_LIBRARY))
+#define GSK_GL_TEXTURE_LIBRARY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass))
 
-G_DECLARE_DERIVABLE_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, GSK, GL_TEXTURE_LIBRARY, GObject)
-
-struct _GskGLTextureAtlas
+typedef struct _GskGLTextureAtlas
 {
+  struct stbrp_context context;
+  struct stbrp_node *nodes;
+
+  int width;
+  int height;
+
   guint texture_id;
-};
 
-struct _GskGLTextureLibraryClass
+  /* Pixels of rects that have been used at some point,
+   * But are now unused.
+   */
+  int unused_pixels;
+
+  void *user_data;
+} GskGLTextureAtlas;
+
+typedef struct _GskGLTextureAtlasEntry
+{
+  /* A backreference to either the atlas or texture containing
+   * the contents of the atlas entry. For larger items, no atlas
+   * is used and instead a direct texture.
+   */
+  union {
+    GskGLTextureAtlas *atlas;
+    GskGLTexture *texture;
+  };
+
+  /* The area within the atlas translated to 0..1 bounds */
+  graphene_rect_t area;
+
+  /* Number of pixels in the entry, used to calculate usage
+   * of an atlas while processing.
+   */
+  guint n_pixels : 29;
+
+  /* If entry has marked pixels as used in the atlas this frame */
+  guint used : 1;
+
+  /* If entry was accessed this frame */
+  guint accessed : 1;
+
+  /* When true, backref is an atlas, otherwise texture */
+  guint is_atlased : 1;
+
+  /* Suffix data that is per-library specific. gpointer used to
+   * guarantee the alignment for the entries using this.
+   */
+  gpointer data[0];
+} GskGLTextureAtlasEntry;
+
+typedef struct _GskGLTextureLibrary
+{
+  GObject        parent_instance;
+  GskNextDriver *driver;
+  GHashTable    *hash_table;
+  guint          max_entry_size;
+} GskGLTextureLibrary;
+
+typedef struct _GskGLTextureLibraryClass
 {
   GObjectClass parent_class;
 
   void (*begin_frame) (GskGLTextureLibrary *library);
   void (*end_frame)   (GskGLTextureLibrary *library);
-};
-
-GdkGLContext *gsk_gl_texture_library_get_context (GskGLTextureLibrary    *self);
-void          gsk_gl_texture_library_set_funcs   (GHashFunc               hash_func,
-                                                  GEqualFunc              equal_func);
-void          gsk_gl_texture_library_begin_frame (GskGLTextureLibrary    *self);
-void          gsk_gl_texture_library_end_frame   (GskGLTextureLibrary    *self);
-gboolean      gsk_gl_texture_library_pack        (GskGLTextureLibrary    *self,
-                                                  gconstpointer           key,
-                                                  gsize                   keylen,
-                                                  float                   width,
-                                                  float                   height,
-                                                  GskGLTextureAtlas     **atlas,
-                                                  graphene_rect_t        *area);
-gboolean      gsk_gl_texture_library_lookup      (GskGLTextureLibrary    *library,
-                                                  gconstpointer           key,
-                                                  GskGLTextureAtlas     **atlas,
-                                                  graphene_rect_t        *area);
-void          gsk_gl_texture_library_upload      (GskGLTextureLibrary    *self,
-                                                  GskGLTextureAtlas      *atlas,
-                                                  const graphene_rect_t  *area,
-                                                  GdkTexture             *texture);
+} 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);
+void     gsk_gl_texture_library_end_frame   (GskGLTextureLibrary    *self);
+gpointer gsk_gl_texture_library_pack        (GskGLTextureLibrary    *self,
+                                             gconstpointer           key,
+                                             gsize                   keylen,
+                                             gsize                   valuelen,
+                                             guint                   width,
+                                             guint                   height);
+void     gsk_gl_texture_library_upload      (GskGLTextureLibrary    *self,
+                                             GskGLTextureAtlas      *atlas,
+                                             const graphene_rect_t  *area,
+                                             GdkTexture             *texture);
+
+static inline void
+gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
+                                  int                n_pixels)
+{
+  self->unused_pixels += n_pixels;
+}
+
+static inline void
+gsk_gl_texture_atlas_mark_used (GskGLTextureAtlas *self,
+                                int                n_pixels)
+{
+  self->unused_pixels -= n_pixels;
+}
+
+static inline gboolean
+gsk_gl_texture_library_lookup (GskGLTextureLibrary     *self,
+                               gconstpointer            key,
+                               GskGLTextureAtlasEntry **out_entry)
+{
+  GskGLTextureAtlasEntry *entry = g_hash_table_lookup (self->hash_table, key);
+
+  if G_LIKELY (entry != NULL && entry->accessed && entry->used)
+    {
+      *out_entry = entry;
+      return TRUE;
+    }
+
+  if (entry != NULL)
+    {
+      if (!entry->used && entry->is_atlased)
+        {
+          g_assert (entry->atlas != NULL);
+          gsk_gl_texture_atlas_mark_used (entry->atlas, entry->n_pixels);
+          entry->used = TRUE;
+        }
+
+      entry->accessed = TRUE;
+      *out_entry = entry;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static inline guint
+GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (gconstpointer d)
+{
+  const GskGLTextureAtlasEntry *e = d;
+
+  return e->is_atlased ? e->atlas->texture_id : e->texture->texture_id;
+}
+
+static inline double
+gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
+{
+  if (self->unused_pixels > 0)
+    return (double)(self->unused_pixels) / (double)(self->width * self->height);
+  return 0.0;
+}
 
 G_END_DECLS
 
diff --git a/gsk/next/gskgltypesprivate.h b/gsk/next/gskgltypesprivate.h
index 03aa07383a..efb2c4c500 100644
--- a/gsk/next/gskgltypesprivate.h
+++ b/gsk/next/gskgltypesprivate.h
@@ -41,6 +41,7 @@ typedef struct _GskGLIconLibrary GskGLIconLibrary;
 typedef struct _GskGLProgram GskGLProgram;
 typedef struct _GskGLRenderJob GskGLRenderJob;
 typedef struct _GskGLShadowLibrary GskGLShadowLibrary;
+typedef struct _GskGLTexture GskGLTexture;
 typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
 typedef struct _GskGLTextureLibrary GskGLTextureLibrary;
 typedef struct _GskGLUniformState GskGLUniformState;


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