[gtk/wip/chergert/glproto] initial work on glyph cache
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/glproto] initial work on glyph cache
- Date: Mon, 18 Jan 2021 19:11:40 +0000 (UTC)
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]