[gtk/wip/chergert/glyphy: 19/20] gsk/gl: upload glyphy arc lists to texture atlas
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/glyphy: 19/20] gsk/gl: upload glyphy arc lists to texture atlas
- Date: Tue, 15 Mar 2022 23:12:02 +0000 (UTC)
commit 5383b8f657848dfd4b727db9b2a9f1a6842f7d5d
Author: Christian Hergert <chergert redhat com>
Date: Mon Mar 14 16:22:52 2022 -0700
gsk/gl: upload glyphy arc lists to texture atlas
Glyphy provides a way to serialize a glyph from a hb_font_t using
the Harfbuzz 4.0 API into a list of arcs. This commit provides a new
texture library subclass to contain those arc lists and integration
to perform basic reclaimation.
Lookups are cached using a 256-entry fronting cache similar to what
was done in the GskGLGlyphLibrary for Cairo rendered fonts.
gsk/gl/gskgldriver.c | 2 +
gsk/gl/gskgldriverprivate.h | 1 +
gsk/gl/gskglglyphylibrary.c | 290 +++++++++++++++++++++++++++++++++++++
gsk/gl/gskglglyphylibraryprivate.h | 101 +++++++++++++
gsk/gl/gskgltypesprivate.h | 1 +
gsk/meson.build | 1 +
6 files changed, 396 insertions(+)
---
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 327dea93ff..712cc59ed0 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -32,6 +32,7 @@
#include "gskglcommandqueueprivate.h"
#include "gskglcompilerprivate.h"
#include "gskglglyphlibraryprivate.h"
+#include "gskglglyphylibraryprivate.h"
#include "gskgliconlibraryprivate.h"
#include "gskglprogramprivate.h"
#include "gskglshadowlibraryprivate.h"
@@ -457,6 +458,7 @@ gsk_gl_driver_new (GskGLCommandQueue *command_queue,
}
self->glyphs = gsk_gl_glyph_library_new (self);
+ self->glyphy = gsk_gl_glyphy_library_new (self);
self->icons = gsk_gl_icon_library_new (self);
self->shadows = gsk_gl_shadow_library_new (self);
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index 4500962ed8..5878591396 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -101,6 +101,7 @@ struct _GskGLDriver
GskGLCommandQueue *command_queue;
GskGLGlyphLibrary *glyphs;
+ GskGLGlyphyLibrary *glyphy;
GskGLIconLibrary *icons;
GskGLShadowLibrary *shadows;
diff --git a/gsk/gl/gskglglyphylibrary.c b/gsk/gl/gskglglyphylibrary.c
new file mode 100644
index 0000000000..5e5406596f
--- /dev/null
+++ b/gsk/gl/gskglglyphylibrary.c
@@ -0,0 +1,290 @@
+/* gskglglyphylibrary.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 <gdk/gdkglcontextprivate.h>
+#include <gdk/gdkmemoryformatprivate.h>
+#include <gdk/gdkprofilerprivate.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskglglyphylibraryprivate.h"
+
+#include <glyphy-harfbuzz.h>
+
+#define TOLERANCE (1/2048.)
+#define MIN_FONT_SIZE 10
+#define ITEM_W 64
+#define ITEM_H_QUANTUM 8
+
+G_DEFINE_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
+
+GskGLGlyphyLibrary *
+gsk_gl_glyphy_library_new (GskGLDriver *driver)
+{
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
+
+ return g_object_new (GSK_TYPE_GL_GLYPHY_LIBRARY,
+ "driver", driver,
+ NULL);
+}
+
+static guint
+gsk_gl_glyphy_key_hash (gconstpointer data)
+{
+ const GskGLGlyphyKey *key = data;
+
+ /* malloc()'d pointers already guarantee 3 bits from the LSB on 64-bit and
+ * 2 bits from the LSB on 32-bit. Shift by enough to give us 256 entries
+ * in our front cache for the glyph since languages will naturally cluster
+ * for us.
+ */
+
+#if GLIB_SIZEOF_VOID_P == 4
+ return (guint)(GPOINTER_TO_SIZE (key->font) << 6) ^ key->glyph;
+#else
+ return (guint)(GPOINTER_TO_SIZE (key->font) << 5) ^ key->glyph;
+#endif
+}
+
+static gboolean
+gsk_gl_glyphy_key_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ return memcmp (v1, v2, sizeof (GskGLGlyphyKey)) == 0;
+}
+
+static void
+gsk_gl_glyphy_key_free (gpointer data)
+{
+ GskGLGlyphyKey *key = data;
+
+ g_clear_object (&key->font);
+ g_slice_free (GskGLGlyphyKey, key);
+}
+
+static void
+gsk_gl_glyphy_value_free (gpointer data)
+{
+ g_slice_free (GskGLGlyphyValue, data);
+}
+
+static void
+gsk_gl_glyphy_library_clear_cache (GskGLTextureLibrary *library)
+{
+ GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)library;
+
+ g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
+
+ memset (self->front, 0, sizeof self->front);
+}
+
+static void
+gsk_gl_glyphy_library_finalize (GObject *object)
+{
+ GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)object;
+
+ g_clear_pointer (&self->acc, glyphy_arc_accumulator_destroy);
+ g_clear_pointer (&self->acc_endpoints, g_array_unref);
+
+ G_OBJECT_CLASS (gsk_gl_glyphy_library_parent_class)->finalize (object);
+}
+
+static void
+gsk_gl_glyphy_library_class_init (GskGLGlyphyLibraryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass);
+
+ object_class->finalize = gsk_gl_glyphy_library_finalize;
+
+ library_class->clear_cache = gsk_gl_glyphy_library_clear_cache;
+}
+
+static void
+gsk_gl_glyphy_library_init (GskGLGlyphyLibrary *self)
+{
+ GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
+
+ tl->max_entry_size = 0;
+ tl->max_frame_age = 512;
+ gsk_gl_texture_library_set_funcs (tl,
+ gsk_gl_glyphy_key_hash,
+ gsk_gl_glyphy_key_equal,
+ gsk_gl_glyphy_key_free,
+ gsk_gl_glyphy_value_free);
+
+ self->acc = glyphy_arc_accumulator_create ();
+ self->acc_endpoints = g_array_new (FALSE, FALSE, sizeof (glyphy_arc_endpoint_t));
+}
+
+static glyphy_bool_t
+accumulate_endpoint (glyphy_arc_endpoint_t *endpoint,
+ GArray *endpoints)
+{
+ g_array_append_vals (endpoints, endpoint, 1);
+ return TRUE;
+}
+
+static inline gboolean
+encode_glyph (GskGLGlyphyLibrary *self,
+ hb_font_t *font,
+ unsigned int glyph_index,
+ double tolerance_per_em,
+ glyphy_rgba_t *buffer,
+ guint buffer_len,
+ guint *output_len,
+ guint *nominal_width,
+ guint *nominal_height,
+ glyphy_extents_t *extents,
+ double *advance,
+ gboolean *is_empty)
+{
+ hb_face_t *face = hb_font_get_face (font);
+ guint upem = hb_face_get_upem (face);
+ double tolerance = upem * tolerance_per_em;
+ double faraway = (double)upem / (MIN_FONT_SIZE * M_SQRT2);
+ double avg_fetch_achieved;
+
+ self->acc_endpoints->len = 0;
+
+ glyphy_arc_accumulator_reset (self->acc);
+ glyphy_arc_accumulator_set_tolerance (self->acc, tolerance);
+ glyphy_arc_accumulator_set_callback (self->acc,
+ (glyphy_arc_endpoint_accumulator_callback_t)accumulate_endpoint,
+ self->acc_endpoints);
+
+ glyphy_harfbuzz(font_get_glyph_shape) (font, glyph_index, self->acc);
+ if (!glyphy_arc_accumulator_successful (self->acc))
+ return FALSE;
+
+ g_assert (glyphy_arc_accumulator_get_error (self->acc) <= tolerance);
+
+ if (!glyphy_arc_list_encode_blob ((gpointer)self->acc_endpoints->data,
+ self->acc_endpoints->len,
+ buffer,
+ buffer_len,
+ faraway,
+ 4, /* UNUSED */
+ &avg_fetch_achieved,
+ output_len,
+ nominal_width,
+ nominal_height,
+ extents))
+ return FALSE;
+
+ glyphy_extents_scale (extents, 1./upem, 1./upem);
+
+ *advance = hb_font_get_glyph_h_advance (font, glyph_index) / (double)upem;
+ *is_empty = glyphy_extents_is_empty (extents);
+
+ return TRUE;
+}
+
+gboolean
+gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
+ GskGLGlyphyKey *key,
+ const GskGLGlyphyValue **out_value)
+{
+ static glyphy_rgba_t buffer[4096 * 16];
+ GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
+ GskGLGlyphyValue *value;
+ glyphy_extents_t extents;
+ hb_font_t *font;
+ gboolean is_empty;
+ double advance;
+ guint packed_x;
+ guint packed_y;
+ guint nominal_w, nominal_h;
+ guint output_len = 0;
+ guint texture_id;
+ guint width, height;
+
+ g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
+ g_assert (key != NULL);
+ g_assert (key->font != NULL);
+ g_assert (out_value != NULL);
+
+ /* Convert the glyph to a list of arcs */
+ font = pango_font_get_hb_font (key->font);
+ if (!encode_glyph (self, font, key->glyph, TOLERANCE,
+ buffer, sizeof buffer, &output_len,
+ &nominal_w, &nominal_h,
+ &extents, &advance, &is_empty))
+ return FALSE;
+
+ /* Allocate space for list within atlas */
+ width = ITEM_W;
+ height = (output_len + width - 1) / width;
+ value = gsk_gl_texture_library_pack (tl, key, sizeof *value,
+ width, height, 0,
+ &packed_x, &packed_y);
+
+ /* Make sure we found space to pack */
+ texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value);
+ if (texture_id == 0)
+ return FALSE;
+
+ if (!is_empty)
+ {
+ /* Connect the texture for data upload */
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_id);
+
+ g_assert (width > 0);
+ g_assert (height > 0);
+
+ /* Upload the arc list */
+ if (width * height == output_len)
+ {
+ glTexSubImage2D (GL_TEXTURE_2D, 0,
+ packed_x, packed_y,
+ width, height,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ buffer);
+ }
+ else
+ {
+ glTexSubImage2D (GL_TEXTURE_2D, 0,
+ packed_x, packed_y,
+ width, height - 1,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ buffer);
+ /* Upload the last row separately */
+ glTexSubImage2D (GL_TEXTURE_2D, 0,
+ packed_x, packed_y + height - 1,
+ output_len - (width * (height - 1)), 1,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ buffer + (width * (height - 1)));
+ }
+ }
+
+ value->advance = advance;
+ value->extents = extents;
+ value->nominal_w = nominal_w;
+ value->nominal_h = nominal_h;
+ value->atlas_x = packed_x / ITEM_W;
+ value->atlas_y = packed_y / ITEM_H_QUANTUM;
+
+ *out_value = value;
+
+ return TRUE;
+}
diff --git a/gsk/gl/gskglglyphylibraryprivate.h b/gsk/gl/gskglglyphylibraryprivate.h
new file mode 100644
index 0000000000..800c1fb0d9
--- /dev/null
+++ b/gsk/gl/gskglglyphylibraryprivate.h
@@ -0,0 +1,101 @@
+/* gskglglyphylibraryprivate.h
+ *
+ * Copyright 2020-2022 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
+ */
+
+#ifndef __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
+#define __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
+
+#include <glyphy.h>
+#include <pango/pango.h>
+
+#include "gskgltexturelibraryprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_GLYPHY_LIBRARY (gsk_gl_glyphy_library_get_type())
+
+typedef struct _GskGLGlyphyKey
+{
+ PangoFont *font;
+ PangoGlyph glyph;
+} GskGLGlyphyKey;
+
+typedef struct _GskGLGlyphyValue
+{
+ GskGLTextureAtlasEntry entry;
+ glyphy_extents_t extents;
+ double advance;
+ guint nominal_w;
+ guint nominal_h;
+ guint atlas_x;
+ guint atlas_y;
+ guint is_empty : 1;
+} GskGLGlyphyValue;
+
+G_DECLARE_FINAL_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK, GL_GLYPHY_LIBRARY, GskGLTextureLibrary)
+
+struct _GskGLGlyphyLibrary
+{
+ GskGLTextureLibrary parent_instance;
+ glyphy_arc_accumulator_t *acc;
+ GArray *acc_endpoints;
+ struct {
+ GskGLGlyphyKey key;
+ const GskGLGlyphyValue *value;
+ } front[256];
+};
+
+GskGLGlyphyLibrary *gsk_gl_glyphy_library_new (GskGLDriver *driver);
+gboolean gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
+ GskGLGlyphyKey *key,
+ const GskGLGlyphyValue **out_value);
+
+static inline guint
+gsk_gl_glyphy_library_lookup_or_add (GskGLGlyphyLibrary *self,
+ const GskGLGlyphyKey *key,
+ const GskGLGlyphyValue **out_value)
+{
+ GskGLTextureAtlasEntry *entry;
+ guint front_index = key->glyph & 0xFF;
+
+ if (memcmp (key, &self->front[front_index], sizeof *key) == 0)
+ {
+ *out_value = self->front[front_index].value;
+ }
+ else if (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry))
+ {
+ *out_value = (GskGLGlyphyValue *)entry;
+ self->front[front_index].key = *key;
+ self->front[front_index].value = *out_value;
+ }
+ else
+ {
+ GskGLGlyphyKey *k = g_slice_copy (sizeof *key, key);
+ g_object_ref (k->font);
+ gsk_gl_glyphy_library_add (self, k, out_value);
+ self->front[front_index].key = *key;
+ self->front[front_index].value = *out_value;
+ }
+
+ return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (*out_value);
+}
+
+G_END_DECLS
+
+#endif /* __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__ */
diff --git a/gsk/gl/gskgltypesprivate.h b/gsk/gl/gskgltypesprivate.h
index 1250756fb7..57364fcf4e 100644
--- a/gsk/gl/gskgltypesprivate.h
+++ b/gsk/gl/gskgltypesprivate.h
@@ -37,6 +37,7 @@ typedef struct _GskGLCompiler GskGLCompiler;
typedef struct _GskGLDrawVertex GskGLDrawVertex;
typedef struct _GskGLRenderTarget GskGLRenderTarget;
typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
+typedef struct _GskGLGlyphyLibrary GskGLGlyphyLibrary;
typedef struct _GskGLIconLibrary GskGLIconLibrary;
typedef struct _GskGLProgram GskGLProgram;
typedef struct _GskGLRenderJob GskGLRenderJob;
diff --git a/gsk/meson.build b/gsk/meson.build
index 1c3cae4a63..99020f48fd 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -45,6 +45,7 @@ gsk_private_sources = files([
'gl/gskglcompiler.c',
'gl/gskgldriver.c',
'gl/gskglglyphlibrary.c',
+ 'gl/gskglglyphylibrary.c',
'gl/gskgliconlibrary.c',
'gl/gskglprogram.c',
'gl/gskglrenderjob.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]