[gtk/wip/matthiasc/shared-glyph-cache: 2/3] Share glyph and icon caches
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/matthiasc/shared-glyph-cache: 2/3] Share glyph and icon caches
- Date: Tue, 4 Jun 2019 21:52:39 +0000 (UTC)
commit 0dd5948286c29c081a5f05fa2962663ddf99fbfc
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Jun 4 21:27:09 2019 +0000
Share glyph and icon caches
Use the same texture atlases to back both
the glyph and icon caches, and unify their
sizes and management. Store big glyphs
in separate textures, so all atlases have
the same size. Tweak some of the eviction
parameters.
We share the caches across all GL contexts
on a display, unless the GSK_NO_SHARED_CACHES
env var is set.
gsk/gl/gskglglyphcache.c | 416 ++++++++++++++++----------------------
gsk/gl/gskglglyphcacheprivate.h | 39 ++--
gsk/gl/gskgliconcache.c | 186 +++++++----------
gsk/gl/gskgliconcacheprivate.h | 14 +-
gsk/gl/gskglrenderer.c | 133 +++++++++---
gsk/gl/gskgltextureatlas.c | 213 ++++++++++++++++++-
gsk/gl/gskgltextureatlasprivate.h | 24 ++-
7 files changed, 608 insertions(+), 417 deletions(-)
---
diff --git a/gsk/gl/gskglglyphcache.c b/gsk/gl/gskglglyphcache.c
index b1338d9e58..6d8872a6cd 100644
--- a/gsk/gl/gskglglyphcache.c
+++ b/gsk/gl/gskglglyphcache.c
@@ -12,20 +12,18 @@
#include <cairo.h>
#include <epoxy/gl.h>
-/* Parameters for our cache eviction strategy.
+/* Cache eviction strategy
*
- * Each cached glyph has an age that gets reset every time a cached glyph gets used.
- * Glyphs that have not been used for the MAX_AGE frames are considered old. We keep
- * count of the pixels of each atlas that are taken up by old glyphs. We check the
- * fraction of old pixels every CHECK_INTERVAL frames, and if it is above MAX_OLD_RATIO, then
- * we drop the atlas an all the glyphs contained in it from the cache.
+ * Each cached glyph has an age that gets reset every time a cached
+ * glyph gets used. Glyphs that have not been used for the
+ * MAX_FRAME_AGE frames are considered old.
+ *
+ * We keep count of the pixels of each atlas that are taken up by old
+ * data. When the fraction of old pixels gets too high, we drop the
+ * atlas and all the items it contained.
*/
-#define MAX_AGE 60
-#define CHECK_INTERVAL 10
-#define MAX_OLD_RATIO 0.333
-
-#define ATLAS_SIZE 512
+#define MAX_FRAME_AGE (5 * 60)
static guint glyph_cache_hash (gconstpointer v);
static gboolean glyph_cache_equal (gconstpointer v1,
@@ -33,59 +31,47 @@ static gboolean glyph_cache_equal (gconstpointer v1,
static void glyph_cache_key_free (gpointer v);
static void glyph_cache_value_free (gpointer v);
-static GskGLTextureAtlas *
-create_atlas (GskGLGlyphCache *self,
- int width,
- int height)
+GskGLGlyphCache *
+gsk_gl_glyph_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
- GskGLTextureAtlas *atlas;
+ GskGLGlyphCache *glyph_cache;
- atlas = g_new (GskGLTextureAtlas, 1);
- gsk_gl_texture_atlas_init (atlas, MAX (width, ATLAS_SIZE), MAX (height, ATLAS_SIZE));
+ glyph_cache = g_new0 (GskGLGlyphCache, 1);
- GSK_NOTE(GLYPH_CACHE, g_message ("Create atlas %d x %d", atlas->width, atlas->height));
-
- return atlas;
-}
+ glyph_cache->display = display;
+ glyph_cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
+ glyph_cache_key_free, glyph_cache_value_free);
-static void
-free_atlas (gpointer v)
-{
- GskGLTextureAtlas *atlas = v;
+ glyph_cache->atlases = gsk_gl_texture_atlases_ref (atlases);
- g_assert (atlas->image.texture_id == 0);
- gsk_gl_texture_atlas_free (atlas);
+ glyph_cache->ref_count = 1;
- g_free (atlas);
+ return glyph_cache;
}
-void
-gsk_gl_glyph_cache_init (GskGLGlyphCache *self)
+GskGLGlyphCache *
+gsk_gl_glyph_cache_ref (GskGLGlyphCache *self)
{
- self->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
- glyph_cache_key_free, glyph_cache_value_free);
- self->atlases = g_ptr_array_new_with_free_func (free_atlas);
+ self->ref_count++;
+
+ return self;
}
void
-gsk_gl_glyph_cache_free (GskGLGlyphCache *self,
- GskGLDriver *driver)
+gsk_gl_glyph_cache_unref (GskGLGlyphCache *self)
{
- guint i;
+ g_assert (self->ref_count > 0);
- for (i = 0; i < self->atlases->len; i ++)
+ if (self->ref_count == 1)
{
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->image.texture_id != 0)
- {
- gsk_gl_image_destroy (&atlas->image, driver);
- atlas->image.texture_id = 0;
- }
+ gsk_gl_texture_atlases_unref (self->atlases);
+ g_hash_table_unref (self->hash_table);
+ g_free (self);
+ return;
}
- g_ptr_array_unref (self->atlases);
- g_hash_table_unref (self->hash_table);
+ self->ref_count--;
}
static gboolean
@@ -122,83 +108,11 @@ glyph_cache_value_free (gpointer v)
g_free (v);
}
-static void
-add_to_cache (GskGLGlyphCache *cache,
- GlyphCacheKey *key,
- GskGLCachedGlyph *value)
-{
- const int width = value->draw_width * key->scale / 1024;
- const int height = value->draw_height * key->scale / 1024;
- GskGLTextureAtlas *atlas = NULL;
- guint i, p;
- int packed_x, packed_y;
-
- /* Try all the atlases and pick the first one that can hold
- * our new glyph */
- for (i = 0, p = cache->atlases->len; i < p; i ++)
- {
- GskGLTextureAtlas *test_atlas = g_ptr_array_index (cache->atlases, i);
- gboolean was_packed;
-
- was_packed = gsk_gl_texture_atlas_pack (test_atlas, width, height, &packed_x, &packed_y);
-
- if (was_packed)
- {
- atlas = test_atlas;
- break;
- }
- }
-
- if (atlas == NULL)
- {
- gboolean was_packed;
-
- atlas = create_atlas (cache, width + 2, height + 2);
-
- g_ptr_array_add (cache->atlases, atlas);
-
- was_packed = gsk_gl_texture_atlas_pack (atlas,
- width + 2, height + 2,
- &packed_x, &packed_y);
-
- g_assert (was_packed);
- }
-
- value->tx = (float)(packed_x + 1) / atlas->width;
- value->ty = (float)(packed_y + 1) / atlas->height;
- value->tw = (float)width / atlas->width;
- value->th = (float)height / atlas->height;
- value->used = TRUE;
-
- value->atlas = atlas;
-
- if (atlas->user_data == NULL)
- atlas->user_data = g_new0 (DirtyGlyph, 1);
-
- ((DirtyGlyph *)atlas->user_data)->key = key;
- ((DirtyGlyph *)atlas->user_data)->value = value;
-
-#ifdef G_ENABLE_DEBUG
- if (GSK_DEBUG_CHECK (GLYPH_CACHE))
- {
- for (i = 0; i < cache->atlases->len; i++)
- {
- atlas = g_ptr_array_index (cache->atlases, i);
- g_message ("atlas %d (%dx%d): %.2g%% old pixels",
- i, atlas->width, atlas->height,
- gsk_gl_texture_atlas_get_unused_ratio (atlas));
- }
- }
-#endif
-}
-
static gboolean
-render_glyph (const GskGLTextureAtlas *atlas,
- const DirtyGlyph *glyph,
- GskImageRegion *region)
+render_glyph (GlyphCacheKey *key,
+ GskGLCachedGlyph *value,
+ GskImageRegion *region)
{
- GlyphCacheKey *key = glyph->key;
- GskGLCachedGlyph *value = glyph->value;
cairo_surface_t *surface;
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
@@ -210,16 +124,14 @@ render_glyph (const GskGLTextureAtlas *atlas,
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->font);
if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
- return FALSE;
+ {
+ g_warning ("Failed to get a font");
+ return FALSE;
+ }
surface_width = value->draw_width * key->scale / 1024;
surface_height = value->draw_height * key->scale / 1024;
- /* TODO: Give glyphs that large their own texture in the proper size. Don't
- * put them in the atlas at all. */
- if (surface_width > atlas->width || surface_height > atlas->height)
- return FALSE;
-
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, surface_width);
data = g_malloc0 (stride * surface_height);
surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
@@ -252,45 +164,120 @@ render_glyph (const GskGLTextureAtlas *atlas,
region->height = cairo_image_surface_get_height (surface);
region->stride = cairo_image_surface_get_stride (surface);
region->data = data;
- region->x = (gsize)(value->tx * atlas->width);
- region->y = (gsize)(value->ty * atlas->height);
+ if (value->atlas)
+ {
+ region->x = (gsize)(value->tx * value->atlas->width);
+ region->y = (gsize)(value->ty * value->atlas->height);
+ }
+ else
+ {
+ region->x = 0;
+ region->y = 0;
+ }
cairo_surface_destroy (surface);
+
return TRUE;
}
static void
-upload_dirty_glyph (GskGLGlyphCache *self,
- GskGLTextureAtlas *atlas,
- GskGLDriver *driver)
+upload_glyph (GlyphCacheKey *key,
+ GskGLCachedGlyph *value)
{
- GskImageRegion region;
-
- g_assert (atlas->user_data != NULL);
+ GskImageRegion r;
- gdk_gl_context_push_debug_group_printf (gsk_gl_driver_get_gl_context (driver),
- "Uploading glyph %d", ((DirtyGlyph
*)atlas->user_data)->key->glyph);
+ gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
+ "Uploading glyph %d",
+ key->glyph);
- if (render_glyph (atlas, (DirtyGlyph *)atlas->user_data, ®ion))
+ if (render_glyph (key, value, &r))
{
+ glBindTexture (GL_TEXTURE_2D, value->texture_id);
+ glTextureSubImage2D (value->texture_id, 0,
+ r.x, r.y, r.width, r.height,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ r.data);
+ g_free (r.data);
+ }
- gsk_gl_image_upload_region (&atlas->image, driver, ®ion);
+ gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
+}
- g_free (region.data);
- }
+static void
+add_to_cache (GskGLGlyphCache *self,
+ GlyphCacheKey *key,
+ GskGLCachedGlyph *value)
+{
+ const int width = value->draw_width * key->scale / 1024;
+ const int height = value->draw_height * key->scale / 1024;
+ GskGLTextureAtlas *atlas = NULL;
+ int packed_x = 0;
+ int packed_y = 0;
+
+ gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
+
+ value->tx = (float)(packed_x + 1) / atlas->width;
+ value->ty = (float)(packed_y + 1) / atlas->height;
+ value->tw = (float)width / atlas->width;
+ value->th = (float)height / atlas->height;
+ value->used = TRUE;
+
+ value->atlas = atlas;
+ value->texture_id = atlas->texture_id;
+
+ upload_glyph (key, value);
+}
+
+void
+gsk_gl_glyph_cache_get_texture (GskGLDriver *driver,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale,
+ GskGLCachedGlyph *value)
+{
+ PangoRectangle ink_rect;
+ GlyphCacheKey key;
+ int width, height;
+ guint texture_id;
+
+ pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ key.font = font;
+ key.glyph = glyph;
+ key.scale = (guint)(scale * 1024);
+
+ value->atlas = NULL;
+ value->timestamp = 0;
+
+ value->draw_x = ink_rect.x;
+ value->draw_y = ink_rect.y;
+ value->draw_width = ink_rect.width;
+ value->draw_height = ink_rect.height;
+
+ value->tx = 0.0f;
+ value->ty = 0.0f;
+ value->tw = 1.0f;
+ value->th = 1.0f;
+
+ width = value->draw_width * key.scale / 1024;
+ height = value->draw_height * key.scale / 1024;
+
+ texture_id = gsk_gl_driver_create_texture (driver, width, height);
+ gsk_gl_driver_bind_source_texture (driver, texture_id);
+ gsk_gl_driver_init_texture_empty (driver, texture_id, GL_NEAREST, GL_NEAREST);
- gdk_gl_context_pop_debug_group (gsk_gl_driver_get_gl_context (driver));
- /* TODO: This could be unnecessary. We can just reuse the allocated
- * DirtyGlyph next time. */
- g_clear_pointer (&atlas->user_data, g_free);
+ value->texture_id = texture_id;
+
+ upload_glyph (&key, value);
}
-const GskGLCachedGlyph *
-gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
- gboolean create,
- PangoFont *font,
- PangoGlyph glyph,
- float scale)
+gboolean
+gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale,
+ GskGLCachedGlyph *cached_glyph_out)
{
GskGLCachedGlyph *value;
@@ -305,7 +292,7 @@ gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
{
const guint age = cache->timestamp - value->timestamp;
- if (MAX_AGE <= age)
+ if (age > MAX_FRAME_AGE)
{
GskGLTextureAtlas *atlas = value->atlas;
@@ -321,116 +308,68 @@ gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
value->timestamp = cache->timestamp;
}
- if (create && value == NULL)
+ if (value == NULL)
{
- GlyphCacheKey *key;
PangoRectangle ink_rect;
- key = g_new0 (GlyphCacheKey, 1);
- value = g_new0 (GskGLCachedGlyph, 1);
-
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL);
+ value = g_new0 (GskGLCachedGlyph, 1);
+
value->draw_x = ink_rect.x;
value->draw_y = ink_rect.y;
value->draw_width = ink_rect.width;
value->draw_height = ink_rect.height;
value->timestamp = cache->timestamp;
value->atlas = NULL; /* For now */
- value->scale = (guint)(scale * 1024);
-
- key->font = g_object_ref (font);
- key->glyph = glyph;
- key->scale = (guint)(scale * 1024);
- if (ink_rect.width > 0 && ink_rect.height > 0 && key->scale > 0)
- add_to_cache (cache, key, value);
-
- g_hash_table_insert (cache->hash_table, key, value);
- }
+ if (ink_rect.width < 128 && ink_rect.height < 128)
+ {
+ GlyphCacheKey *key;
- return value;
-}
+ key = g_new0 (GlyphCacheKey, 1);
-guint
-gsk_gl_glyph_cache_get_glyph_texture_id (GskGLGlyphCache *self,
- GskGLDriver *driver,
- const GskGLCachedGlyph *glyph)
-{
- GskGLTextureAtlas *atlas = glyph->atlas;
+ key->font = g_object_ref (font);
+ key->glyph = glyph;
+ key->scale = (guint)(scale * 1024);
- g_assert (atlas != NULL);
+ if (ink_rect.width > 0 && ink_rect.height > 0 && key->scale > 0)
+ add_to_cache (cache, key, value);
- if (atlas->image.texture_id == 0)
+ *cached_glyph_out = *value;
+ g_hash_table_insert (cache->hash_table, key, value);
+ }
+ else
+ {
+ *cached_glyph_out = *value;
+ glyph_cache_value_free (value);
+ }
+ }
+ else
{
- gsk_gl_image_create (&atlas->image, driver, atlas->width, atlas->height, GL_LINEAR, GL_LINEAR);
- gdk_gl_context_label_object_printf (gsk_gl_driver_get_gl_context (driver),
- GL_TEXTURE, atlas->image.texture_id,
- "Glyph atlas %d", atlas->image.texture_id);
+ *cached_glyph_out = *value;
}
- if (atlas->user_data != NULL)
- upload_dirty_glyph (self, atlas, driver);
-
- return atlas->image.texture_id;
+ return cached_glyph_out->atlas != NULL;
}
void
-gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
- GskGLDriver *driver)
+gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
{
- int i;
GHashTableIter iter;
GlyphCacheKey *key;
GskGLCachedGlyph *value;
- GHashTable *removed = g_hash_table_new (g_direct_hash, g_direct_equal);
guint dropped = 0;
self->timestamp++;
- if ((self->timestamp - 1) % CHECK_INTERVAL != 0)
- return;
-
- /* look for atlases to drop, and create a mapping of updated texture indices */
- for (i = self->atlases->len - 1; i >= 0; i--)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- 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,
- gsk_gl_texture_atlas_get_unused_ratio (atlas)));
-
-#if 0
- static int kk;
-
- g_message ("Dropping glyph cache... Ratio: %f",
- gsk_gl_texture_atlas_get_unused_ratio (atlas));
- gsk_gl_image_write_to_png (&atlas->image, driver,
- g_strdup_printf ("dropped_%d.png", kk++));
-#endif
-
- if (atlas->image.texture_id != 0)
- {
- gsk_gl_image_destroy (&atlas->image, driver);
- atlas->image.texture_id = 0;
- }
-
- g_hash_table_add (removed, atlas);
-
- g_ptr_array_remove_index (self->atlases, i);
- }
- }
-
- /* Remove all glyphs whose atlas was removed, and
- * mark old glyphs as unused
- */
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
- if (g_hash_table_contains (removed, value->atlas))
+ guint pos;
+
+ if (!g_ptr_array_find (self->atlases->atlases, value->atlas, &pos))
{
g_hash_table_iter_remove (&iter);
dropped++;
@@ -439,7 +378,7 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
{
const guint age = self->timestamp - value->timestamp;
- if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
+ if (age > MAX_FRAME_AGE)
{
GskGLTextureAtlas *atlas = value->atlas;
@@ -451,23 +390,6 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
}
}
}
- g_hash_table_unref (removed);
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d glyphs", dropped));
-
-#if 0
- for (i = 0; i < self->atlases->len; i++)
- {
- GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->image)
- {
- char *filename;
-
- filename = g_strdup_printf ("glyphatlas%d-%ld.png", i, self->timestamp);
- gsk_gl_image_write_to_png (atlas->image, driver, filename);
- g_free (filename);
- }
- }
-#endif
}
diff --git a/gsk/gl/gskglglyphcacheprivate.h b/gsk/gl/gskglglyphcacheprivate.h
index c3ab245cf4..72fce67539 100644
--- a/gsk/gl/gskglglyphcacheprivate.h
+++ b/gsk/gl/gskglglyphcacheprivate.h
@@ -9,8 +9,11 @@
typedef struct
{
+ int ref_count;
+
+ GdkDisplay *display;
GHashTable *hash_table;
- GPtrArray *atlases;
+ GskGLTextureAtlases *atlases;
guint64 timestamp;
} GskGLGlyphCache;
@@ -22,18 +25,12 @@ typedef struct
guint scale; /* times 1024 */
} GlyphCacheKey;
-typedef struct _DirtyGlyph DirtyGlyph;
typedef struct _GskGLCachedGlyph GskGLCachedGlyph;
-struct _DirtyGlyph
-{
- GlyphCacheKey *key;
- GskGLCachedGlyph *value;
-};
-
struct _GskGLCachedGlyph
{
GskGLTextureAtlas *atlas;
+ guint texture_id;
float tx;
float ty;
@@ -45,25 +42,25 @@ struct _GskGLCachedGlyph
int draw_width;
int draw_height;
- float scale;
-
guint64 timestamp;
guint used: 1;
};
-void gsk_gl_glyph_cache_init (GskGLGlyphCache *self);
-void gsk_gl_glyph_cache_free (GskGLGlyphCache *self,
- GskGLDriver *driver);
-void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
- GskGLDriver *driver);
-guint gsk_gl_glyph_cache_get_glyph_texture_id (GskGLGlyphCache *self,
- GskGLDriver *driver,
- const GskGLCachedGlyph *glyph);
-const GskGLCachedGlyph * gsk_gl_glyph_cache_lookup (GskGLGlyphCache *self,
- gboolean create,
+GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases);
+GskGLGlyphCache * gsk_gl_glyph_cache_ref (GskGLGlyphCache *self);
+void gsk_gl_glyph_cache_unref (GskGLGlyphCache *self);
+void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self);
+gboolean gsk_gl_glyph_cache_lookup (GskGLGlyphCache *self,
PangoFont *font,
PangoGlyph glyph,
- float scale);
+ float scale,
+ GskGLCachedGlyph *cached_glyph_out);
+void gsk_gl_glyph_cache_get_texture (GskGLDriver *driver,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale,
+ GskGLCachedGlyph *glyph_out);
#endif
diff --git a/gsk/gl/gskgliconcache.c b/gsk/gl/gskgliconcache.c
index cc823f496b..c0ad9af374 100644
--- a/gsk/gl/gskgliconcache.c
+++ b/gsk/gl/gskgliconcache.c
@@ -1,12 +1,11 @@
#include "gskgliconcacheprivate.h"
#include "gskgltextureatlasprivate.h"
#include "gdk/gdktextureprivate.h"
+#include "gdk/gdkglcontextprivate.h"
#include <epoxy/gl.h>
-#define ATLAS_SIZE (1024)
#define MAX_FRAME_AGE (5 * 60)
-#define MAX_UNUSED_RATIO 0.8
typedef struct
{
@@ -22,57 +21,49 @@ icon_data_free (gpointer p)
g_free (p);
}
-static void
-free_atlas (gpointer v)
+GskGLIconCache *
+gsk_gl_icon_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
{
- GskGLTextureAtlas *atlas = v;
+ GskGLIconCache *self;
+
+ self = g_new0 (GskGLIconCache, 1);
- g_assert (atlas->image.texture_id == 0);
- gsk_gl_texture_atlas_free (atlas);
+ self->display = display;
+ self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
+ self->atlases = gsk_gl_texture_atlases_ref (atlases);
+ self->ref_count = 1;
- g_free (atlas);
+ return self;
}
-void
-gsk_gl_icon_cache_init (GskGLIconCache *self,
- GskRenderer *renderer,
- GskGLDriver *gl_driver)
+GskGLIconCache *
+gsk_gl_icon_cache_ref (GskGLIconCache *self)
{
- self->renderer = renderer;
- self->gl_driver = gl_driver;
+ self->ref_count++;
- self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)free_atlas);
- self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
+ return self;
}
void
-gsk_gl_icon_cache_free (GskGLIconCache *self)
+gsk_gl_icon_cache_unref (GskGLIconCache *self)
{
- guint i, p;
+ g_assert (self->ref_count > 0);
- for (i = 0, p = self->atlases->len; i < p; i ++)
+ if (self->ref_count == 1)
{
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->image.texture_id != 0)
- {
- gsk_gl_image_destroy (&atlas->image, self->gl_driver);
- atlas->image.texture_id = 0;
- }
-
- gsk_gl_texture_atlas_free (atlas);
-
- g_free (atlas);
+ gsk_gl_texture_atlases_unref (self->atlases);
+ g_hash_table_unref (self->icons);
+ g_free (self);
+ return;
}
- g_ptr_array_free (self->atlases, TRUE);
- g_hash_table_unref (self->icons);
+ self->ref_count--;
}
void
gsk_gl_icon_cache_begin_frame (GskGLIconCache *self)
{
- gint i, p;
GHashTableIter iter;
GdkTexture *texture;
IconData *icon_data;
@@ -81,45 +72,30 @@ gsk_gl_icon_cache_begin_frame (GskGLIconCache *self)
g_hash_table_iter_init (&iter, self->icons);
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
{
- icon_data->frame_age ++;
+ guint pos;
- if (icon_data->frame_age > MAX_FRAME_AGE)
+ if (!g_ptr_array_find (self->atlases->atlases, icon_data->atlas, &pos))
{
-
- if (icon_data->used)
- {
- const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
- const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
-
- gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
- icon_data->used = FALSE;
- }
- /* We do NOT remove the icon here. Instead, We wait until we drop the entire atlas.
- * This way we can revive it when we use it again. */
+ g_hash_table_iter_remove (&iter);
}
- }
-
- for (i = 0, p = self->atlases->len; i < p; i ++)
- {
- GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_UNUSED_RATIO)
+ else
{
- g_hash_table_iter_init (&iter, self->icons);
- while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
- {
- if (icon_data->atlas == atlas)
- g_hash_table_iter_remove (&iter);
- }
+ icon_data->frame_age ++;
- if (atlas->image.texture_id != 0)
+ if (icon_data->frame_age > MAX_FRAME_AGE)
{
- gsk_gl_image_destroy (&atlas->image, self->gl_driver);
- atlas->image.texture_id = 0;
- }
- g_ptr_array_remove_index_fast (self->atlases, i);
- i --; /* Check the current index again */
+ if (icon_data->used)
+ {
+ const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
+ const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
+
+ gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
+ icon_data->used = FALSE;
+ }
+ /* We do NOT remove the icon here. Instead, We wait until we drop the entire atlas.
+ * This way we can revive it when we use it again. */
+ }
}
}
}
@@ -154,6 +130,16 @@ pad_surface (cairo_surface_t *surface)
return padded;
}
+static void
+upload_region_or_else (GskGLIconCache *self,
+ guint texture_id,
+ GskImageRegion *region)
+{
+ glBindTexture (GL_TEXTURE_2D, texture_id);
+ glTextureSubImage2D (texture_id, 0, region->x, region->y, region->width, region->height,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data);
+}
+
void
gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
@@ -167,84 +153,60 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
icon_data->frame_age = 0;
if (!icon_data->used)
{
- const int w = icon_data->texture_rect.size.width * ATLAS_SIZE;
- const int h = icon_data->texture_rect.size.height * ATLAS_SIZE;
+ const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
+ const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
gsk_gl_texture_atlas_mark_used (icon_data->atlas, w + 2, h + 2);
icon_data->used = TRUE;
}
- *out_texture_id = icon_data->atlas->image.texture_id;
+ *out_texture_id = icon_data->atlas->texture_id;
*out_texture_rect = icon_data->texture_rect;
return;
}
/* texture not on any atlas yet. Find a suitable one. */
{
- const int twidth = gdk_texture_get_width (texture);
- const int theight = gdk_texture_get_height (texture);
- int packed_x, packed_y;
+ const int width = gdk_texture_get_width (texture);
+ const int height = gdk_texture_get_height (texture);
GskGLTextureAtlas *atlas = NULL;
- guint i, p;
+ int packed_x = 0;
+ int packed_y = 0;
GskImageRegion region;
cairo_surface_t *surface;
cairo_surface_t *padded_surface;
- g_assert (twidth < ATLAS_SIZE);
- g_assert (theight < ATLAS_SIZE);
-
- for (i = 0, p = self->atlases->len; i < p; i ++)
- {
- atlas = g_ptr_array_index (self->atlases, i);
-
- if (gsk_gl_texture_atlas_pack (atlas, twidth + 2, theight + 2, &packed_x, &packed_y))
- {
- packed_x += 1;
- packed_y += 1;
- break;
- }
-
- atlas = NULL;
- }
-
- if (!atlas)
- {
- /* 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_image_create (&atlas->image, self->gl_driver, atlas->width, atlas->height, GL_LINEAR,
GL_LINEAR);
- /* Pack it onto that one, which surely has enought space... */
- gsk_gl_texture_atlas_pack (atlas, twidth + 2, theight + 2, &packed_x, &packed_y);
- packed_x += 1;
- packed_y += 1;
-
- g_ptr_array_add (self->atlases, atlas);
- }
+ gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
icon_data = g_new0 (IconData, 1);
icon_data->atlas = atlas;
icon_data->frame_age = 0;
icon_data->used = TRUE;
graphene_rect_init (&icon_data->texture_rect,
- (float)packed_x / ATLAS_SIZE,
- (float)packed_y / ATLAS_SIZE,
- (float)twidth / ATLAS_SIZE,
- (float)theight / ATLAS_SIZE);
+ (float)(packed_x + 1) / atlas->width,
+ (float)(packed_y + 1) / atlas->height,
+ (float)width / atlas->width,
+ (float)height / atlas->height);
g_hash_table_insert (self->icons, texture, icon_data);
/* actually upload the texture */
surface = gdk_texture_download_surface (texture);
padded_surface = pad_surface (surface);
- region.x = packed_x - 1;
- region.y = packed_y - 1;
- region.width = twidth + 2;
- region.height = theight + 2;
+ region.x = packed_x;
+ region.y = packed_y;
+ region.width = width + 2;
+ region.height = height + 2;
region.data = cairo_image_surface_get_data (padded_surface);
- gsk_gl_image_upload_region (&atlas->image, self->gl_driver, ®ion);
+ gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
+ "Uploading texture");
+
+ upload_region_or_else (self, atlas->texture_id, ®ion);
+
+ gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
- *out_texture_id = atlas->image.texture_id;
+ *out_texture_id = atlas->texture_id;
*out_texture_rect = icon_data->texture_rect;
cairo_surface_destroy (surface);
diff --git a/gsk/gl/gskgliconcacheprivate.h b/gsk/gl/gskgliconcacheprivate.h
index 883a274ded..ade371b930 100644
--- a/gsk/gl/gskgliconcacheprivate.h
+++ b/gsk/gl/gskgliconcacheprivate.h
@@ -10,18 +10,20 @@
typedef struct
{
+ int ref_count;
+
+ GdkDisplay *display;
GskGLDriver *gl_driver;
- GskRenderer *renderer;
- GPtrArray *atlases;
+ GskGLTextureAtlases *atlases;
GHashTable *icons; /* GdkTexture -> IconData */
} GskGLIconCache;
-void gsk_gl_icon_cache_init (GskGLIconCache *self,
- GskRenderer *renderer,
- GskGLDriver *gl_driver);
-void gsk_gl_icon_cache_free (GskGLIconCache *self);
+GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display,
+ GskGLTextureAtlases *atlases);
+GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self);
+void gsk_gl_icon_cache_unref (GskGLIconCache *self);
void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self);
void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index c2f5e0abde..a288fea50b 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -335,8 +335,9 @@ struct _GskGLRenderer
RenderOpBuilder op_builder;
GArray *render_ops;
- GskGLGlyphCache glyph_cache;
- GskGLIconCache icon_cache;
+ GskGLTextureAtlases *atlases;
+ GskGLGlyphCache *glyph_cache;
+ GskGLIconCache *icon_cache;
GskGLShadowCache shadow_cache;
#ifdef G_ENABLE_DEBUG
@@ -571,7 +572,7 @@ render_text_node (GskGLRenderer *self,
for (i = 0; i < num_glyphs; i++)
{
const PangoGlyphInfo *gi = &glyphs[i];
- const GskGLCachedGlyph *glyph;
+ GskGLCachedGlyph glyph;
float glyph_x, glyph_y, glyph_w, glyph_h;
float tx, ty, tx2, ty2;
double cx;
@@ -580,32 +581,41 @@ render_text_node (GskGLRenderer *self,
if (gi->glyph == PANGO_GLYPH_EMPTY)
continue;
- glyph = gsk_gl_glyph_cache_lookup (&self->glyph_cache,
- TRUE,
- (PangoFont *)font,
- gi->glyph,
- text_scale);
+ gsk_gl_glyph_cache_lookup (self->glyph_cache,
+ (PangoFont *)font,
+ gi->glyph,
+ text_scale,
+ &glyph);
/* e.g. whitespace */
- if (glyph->draw_width <= 0 || glyph->draw_height <= 0 || glyph->scale <= 0)
+ if (glyph.draw_width <= 0 || glyph.draw_height <= 0)
goto next;
+ /* big glyphs are not cached */
+ if (!glyph.texture_id)
+ {
+ gsk_gl_glyph_cache_get_texture (self->gl_driver,
+ (PangoFont *)font,
+ gi->glyph,
+ text_scale,
+ &glyph);
+ g_assert (glyph.texture_id != 0);
+ }
+
cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
- ops_set_texture (builder, gsk_gl_glyph_cache_get_glyph_texture_id (&self->glyph_cache,
- self->gl_driver,
- glyph));
+ ops_set_texture (builder, glyph.texture_id);
- tx = glyph->tx;
- ty = glyph->ty;
- tx2 = tx + glyph->tw;
- ty2 = ty + glyph->th;
+ tx = glyph.tx;
+ ty = glyph.ty;
+ tx2 = tx + glyph.tw;
+ ty2 = ty + glyph.th;
- glyph_x = x + cx + glyph->draw_x;
- glyph_y = y + cy + glyph->draw_y;
- glyph_w = glyph->draw_width;
- glyph_h = glyph->draw_height;
+ glyph_x = x + cx + glyph.draw_x;
+ glyph_y = y + cy + glyph.draw_y;
+ glyph_w = glyph.draw_width;
+ glyph_h = glyph.draw_height;
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
{ { glyph_x, glyph_y }, { tx, ty }, },
@@ -829,12 +839,12 @@ render_texture_node (GskGLRenderer *self,
int texture_id;
float tx = 0, ty = 0, tx2 = 1, ty2 = 1;
- if (texture->width <= 64 &&
- texture->height <= 64)
+ if (texture->width <= 128 &&
+ texture->height <= 128)
{
graphene_rect_t trect;
- gsk_gl_icon_cache_lookup_or_add (&self->icon_cache,
+ gsk_gl_icon_cache_lookup_or_add (self->icon_cache,
texture,
&texture_id,
&trect);
@@ -2466,6 +2476,68 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
return TRUE;
}
+static GskGLTextureAtlases *
+get_texture_atlases_for_display (GdkDisplay *display)
+{
+ GskGLTextureAtlases *atlases;
+
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_texture_atlases_new ();
+
+ atlases = (GskGLTextureAtlases*)g_object_get_data (G_OBJECT (display), "gsk-gl-texture-atlases");
+ if (atlases == NULL)
+ {
+ atlases = gsk_gl_texture_atlases_new ();
+ g_object_set_data_full (G_OBJECT (display), "gsk-gl-texture-atlases",
+ gsk_gl_texture_atlases_ref (atlases),
+ (GDestroyNotify) gsk_gl_texture_atlases_unref);
+ }
+
+ return atlases;
+}
+
+static GskGLGlyphCache *
+get_glyph_cache_for_display (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
+{
+ GskGLGlyphCache *glyph_cache;
+
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_glyph_cache_new (display, atlases);
+
+ glyph_cache = (GskGLGlyphCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-glyph-cache");
+ if (glyph_cache == NULL)
+ {
+ glyph_cache = gsk_gl_glyph_cache_new (display, atlases);
+ g_object_set_data_full (G_OBJECT (display), "gsk-gl-glyph-cache",
+ gsk_gl_glyph_cache_ref (glyph_cache),
+ (GDestroyNotify) gsk_gl_glyph_cache_unref);
+ }
+
+ return glyph_cache;
+}
+
+static GskGLIconCache *
+get_icon_cache_for_display (GdkDisplay *display,
+ GskGLTextureAtlases *atlases)
+{
+ GskGLIconCache *icon_cache;
+
+ if (g_getenv ("GSK_NO_SHARED_CACHES"))
+ return gsk_gl_icon_cache_new (display, atlases);
+
+ icon_cache = (GskGLIconCache*)g_object_get_data (G_OBJECT (display), "gsk-gl-icon-cache");
+ if (icon_cache == NULL)
+ {
+ icon_cache = gsk_gl_icon_cache_new (display, atlases);
+ g_object_set_data_full (G_OBJECT (display), "gsk-gl-icon-cache",
+ gsk_gl_icon_cache_ref (icon_cache),
+ (GDestroyNotify) gsk_gl_icon_cache_unref);
+ }
+
+ return icon_cache;
+}
+
static gboolean
gsk_gl_renderer_realize (GskRenderer *renderer,
GdkSurface *surface,
@@ -2496,8 +2568,9 @@ gsk_gl_renderer_realize (GskRenderer *renderer,
if (!gsk_gl_renderer_create_programs (self, error))
return FALSE;
- gsk_gl_glyph_cache_init (&self->glyph_cache);
- gsk_gl_icon_cache_init (&self->icon_cache, renderer, self->gl_driver);
+ self->atlases = get_texture_atlases_for_display (gdk_surface_get_display (surface));
+ self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases);
+ self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases);
gsk_gl_shadow_cache_init (&self->shadow_cache);
return TRUE;
@@ -2522,8 +2595,9 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
for (i = 0; i < GL_N_PROGRAMS; i ++)
glDeleteProgram (self->programs[i].id);
- gsk_gl_glyph_cache_free (&self->glyph_cache, self->gl_driver);
- gsk_gl_icon_cache_free (&self->icon_cache);
+ g_clear_pointer (&self->glyph_cache, gsk_gl_glyph_cache_unref);
+ g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref);
+ g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref);
gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
g_clear_object (&self->gl_profiler);
@@ -3089,8 +3163,9 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
ORTHO_FAR_PLANE);
graphene_matrix_scale (&projection, 1, -1, 1);
- gsk_gl_glyph_cache_begin_frame (&self->glyph_cache, self->gl_driver);
- gsk_gl_icon_cache_begin_frame (&self->icon_cache);
+ gsk_gl_texture_atlases_begin_frame (self->atlases);
+ gsk_gl_glyph_cache_begin_frame (self->glyph_cache);
+ gsk_gl_icon_cache_begin_frame (self->icon_cache);
gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
ops_set_projection (&self->op_builder, &projection);
diff --git a/gsk/gl/gskgltextureatlas.c b/gsk/gl/gskgltextureatlas.c
index 3419a2d5a2..06f6fcbedc 100644
--- a/gsk/gl/gskgltextureatlas.c
+++ b/gsk/gl/gskgltextureatlas.c
@@ -1,6 +1,169 @@
+#include "config.h"
#include "gskgltextureatlasprivate.h"
+#include "gskdebugprivate.h"
+#include "gdkglcontextprivate.h"
+#include <epoxy/gl.h>
+#define ATLAS_SIZE (512)
+#define MAX_OLD_RATIO 0.5
+
+static void
+free_atlas (gpointer v)
+{
+ GskGLTextureAtlas *atlas = v;
+
+ gsk_gl_texture_atlas_free (atlas);
+
+ g_free (atlas);
+}
+
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_new (void)
+{
+ GskGLTextureAtlases *atlases;
+
+ atlases = g_new (GskGLTextureAtlases, 1);
+ atlases->atlases = g_ptr_array_new_with_free_func (free_atlas);
+
+ atlases->ref_count = 1;
+
+ return atlases;
+}
+
+GskGLTextureAtlases *
+gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases)
+{
+ atlases->ref_count++;
+
+ return atlases;
+}
+
+void
+gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases)
+{
+ g_assert (atlases->ref_count > 0);
+
+ if (atlases->ref_count == 1)
+ {
+ g_ptr_array_unref (atlases->atlases);
+ g_free (atlases);
+ return;
+ }
+
+ atlases->ref_count--;
+}
+
+#if 1
+static void
+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
+
+void
+gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases)
+{
+ int i;
+
+ for (i = atlases->atlases->len - 1; i >= 0; i--)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (atlases->atlases, i);
+
+ 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,
+ gsk_gl_texture_atlas_get_unused_ratio (atlas)));
+
+ if (atlas->texture_id != 0)
+ {
+ glDeleteTextures (1, &atlas->texture_id);
+ atlas->texture_id = 0;
+ }
+
+ g_ptr_array_remove_index (atlases->atlases, i);
+ }
+ }
+
+#if 1
+ {
+ static guint timestamp;
+
+ timestamp++;
+ if (timestamp % 10 == 0)
+ for (i = 0; i < atlases->atlases->len; i++)
+ {
+ GskGLTextureAtlas *atlas = g_ptr_array_index (atlases->atlases, 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 *atlases,
+ int width,
+ int height,
+ GskGLTextureAtlas **atlas_out,
+ int *out_x,
+ int *out_y)
+{
+ GskGLTextureAtlas *atlas;
+ int x, y;
+ int i;
+
+ g_assert (width < ATLAS_SIZE);
+ g_assert (height < ATLAS_SIZE);
+
+ for (i = 0; i < atlases->atlases->len; i++)
+ {
+ atlas = g_ptr_array_index (atlases->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 = g_malloc (sizeof (GskGLTextureAtlas));
+ gsk_gl_texture_atlas_init (atlas, ATLAS_SIZE, ATLAS_SIZE);
+ gsk_gl_texture_atlas_realize (atlas);
+ g_ptr_array_add (atlases->atlases, atlas);
+
+ /* Pack it onto that one, which surely has enough space... */
+ gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y);
+ }
+
+ *atlas_out = atlas;
+ *out_x = x;
+ *out_y = y;
+
+ return TRUE;
+}
void
gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
@@ -9,7 +172,7 @@ gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
{
memset (self, 0, sizeof (*self));
- self->image.texture_id = 0;
+ self->texture_id = 0;
self->width = width;
self->height = height;
@@ -20,11 +183,19 @@ gsk_gl_texture_atlas_init (GskGLTextureAtlas *self,
width, height,
self->nodes,
width);
+
+ gsk_gl_texture_atlas_realize (self);
}
void
gsk_gl_texture_atlas_free (GskGLTextureAtlas *self)
{
+ if (self->texture_id != 0)
+ {
+ glDeleteTextures (1, &self->texture_id);
+ self->texture_id = 0;
+ }
+
g_clear_pointer (&self->nodes, g_free);
}
@@ -82,3 +253,43 @@ gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self)
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,
+ "Glyph atlas %d", atlas->texture_id);
+}
diff --git a/gsk/gl/gskgltextureatlasprivate.h b/gsk/gl/gskgltextureatlasprivate.h
index d3aea54142..2864fd8d05 100644
--- a/gsk/gl/gskgltextureatlasprivate.h
+++ b/gsk/gl/gskgltextureatlasprivate.h
@@ -14,7 +14,7 @@ struct _GskGLTextureAtlas
int width;
int height;
- GskGLImage image;
+ guint texture_id;
int unused_pixels; /* Pixels of rects that have been used at some point,
But are now unused. */
@@ -23,12 +23,34 @@ struct _GskGLTextureAtlas
};
typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
+struct _GskGLTextureAtlases
+{
+ int ref_count;
+
+ GPtrArray *atlases;
+};
+typedef struct _GskGLTextureAtlases GskGLTextureAtlases;
+
+GskGLTextureAtlases *gsk_gl_texture_atlases_new (void);
+GskGLTextureAtlases *gsk_gl_texture_atlases_ref (GskGLTextureAtlases *atlases);
+void gsk_gl_texture_atlases_unref (GskGLTextureAtlases *atlases);
+
+void gsk_gl_texture_atlases_begin_frame (GskGLTextureAtlases *atlases);
+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);
+
void gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
int width,
int height);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]