[gtk/wip/chergert/glproto] initial work on glyph cache



commit 1e0eb1278a98e8e244e4d24221bd5ff2189dfd75
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jan 18 11:11:33 2021 -0800

    initial work on glyph cache
    
    still requires work on adding glyphs and tracking lifetimes. but this
    gets the consumption plumbing working.

 gsk/next/gskglglyphlibrary.c        |  72 ++++++++++++++++++++--
 gsk/next/gskglglyphlibraryprivate.h |  83 ++++++++++++++++++++++++-
 gsk/next/gskglrenderjob.c           | 118 +++++++++++++++++++++++++++++++++++-
 3 files changed, 266 insertions(+), 7 deletions(-)
---
diff --git a/gsk/next/gskglglyphlibrary.c b/gsk/next/gskglglyphlibrary.c
index ed40f1af7f..858d72cfcb 100644
--- a/gsk/next/gskglglyphlibrary.c
+++ b/gsk/next/gskglglyphlibrary.c
@@ -22,11 +22,6 @@
 
 #include "gskglglyphlibraryprivate.h"
 
-struct _GskGLGlyphLibrary
-{
-  GskGLTextureLibrary parent_instance;
-};
-
 G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
 
 GskGLGlyphLibrary *
@@ -39,12 +34,79 @@ gsk_gl_glyph_library_new (GdkGLContext *context)
                        NULL);
 }
 
+static guint
+gsk_gl_glyph_key_hash (gconstpointer data)
+{
+  const GskGLGlyphKey *key = data;
+
+  /* We do not store the hash within the key because GHashTable will already
+   * store the hash value for us and so this is called only a single time per
+   * cached item. This saves an extra 4 bytes per GskGLGlyphKey which means on
+   * 64-bit, we fit nicely within 2 pointers (the smallest allocation size
+   * for GSlice).
+   */
+
+  return GPOINTER_TO_UINT (key->font) ^
+         key->glyph ^
+         (key->xshift << 24) ^
+         (key->yshift << 26) ^
+         key->scale;
+}
+
+static gboolean
+gsk_gl_glyph_key_equal (gconstpointer v1,
+                        gconstpointer v2)
+{
+  return memcmp (v1, v2, sizeof (GskGLGlyphKey)) == 0;
+}
+
+static void
+gsk_gl_glyph_key_free (gpointer data)
+{
+  g_slice_free (GskGLGlyphKey, data);
+}
+
+static void
+gsk_gl_glyph_value_free (gpointer data)
+{
+  g_slice_free (GskGLGlyphValue, data);
+}
+
+static void
+gsk_gl_glyph_library_finalize (GObject *object)
+{
+  GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)object;
+
+  g_clear_pointer (&self->hash_table, g_hash_table_unref);
+
+  G_OBJECT_CLASS (gsk_gl_glyph_library_parent_class)->finalize (object);
+}
+
 static void
 gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gsk_gl_glyph_library_finalize;
 }
 
 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);
+}
+
+gboolean
+gsk_gl_glyph_library_add (GskGLGlyphLibrary      *self,
+                          const GskGLGlyphKey    *key,
+                          const GskGLGlyphValue **out_value)
+{
+  g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+  g_assert (key != NULL);
+  g_assert (out_value != NULL);
+
+  return FALSE;
 }
diff --git a/gsk/next/gskglglyphlibraryprivate.h b/gsk/next/gskglglyphlibraryprivate.h
index 1929bc9ad4..dd17c516c3 100644
--- a/gsk/next/gskglglyphlibraryprivate.h
+++ b/gsk/next/gskglglyphlibraryprivate.h
@@ -42,11 +42,92 @@ G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 16);
 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)
 
-GskGLGlyphLibrary *gsk_gl_glyph_library_new (GdkGLContext *context);
+struct _GskGLGlyphLibrary
+{
+  GskGLTextureLibrary parent_instance;
+  GHashTable *hash_table;
+};
+
+GskGLGlyphLibrary *gsk_gl_glyph_library_new (GdkGLContext           *context);
+gboolean           gsk_gl_glyph_library_add (GskGLGlyphLibrary      *self,
+                                             const GskGLGlyphKey    *key,
+                                             const GskGLGlyphValue **out_value);
+
+static inline int
+gsk_gl_glyph_key_phase (float value)
+{
+  return floor (4 * (value + 0.125)) - 4 * floor (value + 0.125);
+}
+
+static inline void
+gsk_gl_glyph_key_set_glyph_and_shift (GskGLGlyphKey *key,
+                                      PangoGlyph     glyph,
+                                      float          x,
+                                      float          y)
+{
+  key->glyph = glyph;
+  key->xshift = gsk_gl_glyph_key_phase (x);
+  key->yshift = gsk_gl_glyph_key_phase (y);
+}
+
+static inline gboolean
+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;
+    }
+
+  /* We found it, but haven't marked as used for this frame */
+  if (value != NULL)
+    {
+      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;
+    }
+
+  return gsk_gl_glyph_library_add (self, key, out_value);
+}
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 6c52a442a6..995b3c927a 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -34,6 +34,7 @@
 
 #include "gskglcommandqueueprivate.h"
 #include "gskgldriverprivate.h"
+#include "gskglglyphlibraryprivate.h"
 #include "gskglprogramprivate.h"
 #include "gskglrenderjobprivate.h"
 #include "gskglshadowlibraryprivate.h"
@@ -2478,7 +2479,122 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
                                    const GdkRGBA  *color,
                                    gboolean        force_color)
 {
-  gsk_gl_render_job_visit_as_fallback (job, node);
+  const PangoFont *font = gsk_text_node_get_font (node);
+  const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
+  const graphene_point_t *offset = gsk_text_node_get_offset (node);
+  float text_scale = MAX (job->scale_x, job->scale_y); /* TODO: Fix for uneven scales? */
+  guint num_glyphs = gsk_text_node_get_num_glyphs (node);
+  float x = offset->x + job->offset_x;
+  float y = offset->y + job->offset_y;
+  GskGLGlyphLibrary *library = job->driver->glyphs;
+  GskGLProgram *program;
+  int x_position = 0;
+  GskGLGlyphKey lookup;
+  guint last_texture = 0;
+
+  /* If the font has color glyphs, we don't need to recolor anything */
+  if (!force_color && gsk_text_node_has_color_glyphs (node))
+    {
+      program = job->driver->blit;
+    }
+  else
+    {
+      program = job->driver->coloring;
+      gsk_gl_program_set_uniform_color (program, UNIFORM_COLORING_COLOR, color);
+    }
+
+  memset (&lookup, 0, sizeof lookup);
+  lookup.font = (PangoFont *)font;
+  lookup.scale = (guint) (text_scale * 1024);
+
+  gsk_gl_program_begin_draw (program,
+                             &job->viewport,
+                             &job->projection,
+                             gsk_gl_render_job_get_modelview_matrix (job),
+                             gsk_gl_render_job_get_clip (job),
+                             job->alpha);
+
+  /* We use one quad per character */
+  for (guint i = 0; i < num_glyphs; i++)
+    {
+      GskGLDrawVertex *vertices;
+      const PangoGlyphInfo *gi = &glyphs[i];
+      const GskGLGlyphValue *glyph;
+      float glyph_x, glyph_y, glyph_x2, glyph_y2;
+      float tx, ty, tx2, ty2;
+      float cx;
+      float cy;
+
+      if (gi->glyph == PANGO_GLYPH_EMPTY)
+        continue;
+
+      cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+      cy = (float)(gi->geometry.y_offset) / PANGO_SCALE;
+
+      gsk_gl_glyph_key_set_glyph_and_shift (&lookup, gi->glyph, x + cx, y + cy);
+
+      if (!gsk_gl_glyph_library_lookup_or_add (library, &lookup, &glyph))
+        goto next;
+
+      g_assert (glyph->texture_id != 0);
+
+      if (last_texture != glyph->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;
+        }
+
+      tx  = glyph->tx;
+      ty  = glyph->ty;
+      tx2 = tx + glyph->tw;
+      ty2 = ty + glyph->th;
+
+      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;
+
+      vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+
+      vertices[0].position[0] = glyph_x;
+      vertices[0].position[1] = glyph_y;
+      vertices[0].uv[0] = tx;
+      vertices[0].uv[1] = ty;
+
+      vertices[1].position[0] = glyph_x;
+      vertices[1].position[1] = glyph_y2;
+      vertices[1].uv[0] = tx;
+      vertices[1].uv[1] = ty2;
+
+      vertices[2].position[0] = glyph_x2;
+      vertices[2].position[1] = glyph_y;
+      vertices[2].uv[0] = tx2;
+      vertices[2].uv[1] = ty;
+
+      vertices[3].position[0] = glyph_x2;
+      vertices[3].position[1] = glyph_y2;
+      vertices[3].uv[0] = tx2;
+      vertices[3].uv[1] = ty2;
+
+      vertices[4].position[0] = glyph_x;
+      vertices[4].position[1] = glyph_y2;
+      vertices[4].uv[0] = tx;
+      vertices[4].uv[1] = ty2;
+
+      vertices[4].position[0] = glyph_x2;
+      vertices[4].position[1] = glyph_y;
+      vertices[4].uv[0] = tx2;
+      vertices[4].uv[1] = ty;
+
+next:
+      x_position += gi->geometry.width;
+    }
+
+  gsk_gl_program_end_draw (program);
 }
 
 static void


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