forked from AuroraMiddleware/gtk
Make the glyph cache grow as needed
Make it possible to have more than one texture in the glyph cache, and create new ones when we run out of space in the existing ones.
This commit is contained in:
parent
cead00757d
commit
baeff2a49b
@ -104,14 +104,14 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
|
||||
guint num_glyphs)
|
||||
{
|
||||
GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
|
||||
int i
|
||||
int i;
|
||||
int count = 0;
|
||||
int x_position = 0;
|
||||
|
||||
for (i = 0; i < start_glyph; i++)
|
||||
x_position += glyphs->glyphs[i].geometry.width;
|
||||
|
||||
for (; i < num_glyphs; i++)
|
||||
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
|
||||
{
|
||||
PangoGlyphInfo *gi = &glyphs->glyphs[i];
|
||||
|
||||
|
@ -8,16 +8,18 @@
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
typedef struct {
|
||||
cairo_surface_t *surface;
|
||||
GskVulkanImage *image;
|
||||
int width, height;
|
||||
int x, y, y0;
|
||||
} Atlas;
|
||||
|
||||
struct _GskVulkanGlyphCache {
|
||||
GObject parent_instance;
|
||||
|
||||
GHashTable *hash_table;
|
||||
|
||||
cairo_surface_t *surface;
|
||||
int width, height;
|
||||
int x, y, y0;
|
||||
|
||||
GskVulkanImage *image;
|
||||
GPtrArray *atlases;
|
||||
};
|
||||
|
||||
struct _GskVulkanGlyphCacheClass {
|
||||
@ -32,17 +34,41 @@ static gboolean glyph_cache_equal (gconstpointer v1,
|
||||
static void glyph_cache_key_free (gpointer v);
|
||||
static void glyph_cache_value_free (gpointer v);
|
||||
|
||||
static Atlas *
|
||||
create_atlas (void)
|
||||
{
|
||||
Atlas *atlas;
|
||||
|
||||
atlas = g_new (Atlas, 1);
|
||||
atlas->width = 512;
|
||||
atlas->height = 512;
|
||||
atlas->y0 = 1;
|
||||
atlas->y = 1;
|
||||
atlas->x = 1;
|
||||
atlas->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, atlas->width, atlas->height);
|
||||
atlas->image = NULL;
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
static void
|
||||
free_atlas (gpointer v)
|
||||
{
|
||||
Atlas *atlas = v;
|
||||
|
||||
if (atlas->surface)
|
||||
cairo_surface_destroy (atlas->surface);
|
||||
g_clear_object (&atlas->image);
|
||||
g_free (atlas);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_glyph_cache_init (GskVulkanGlyphCache *cache)
|
||||
{
|
||||
cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
|
||||
glyph_cache_key_free, glyph_cache_value_free);
|
||||
cache->width = 1024;
|
||||
cache->height = 1024;
|
||||
cache->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cache->width, cache->height);
|
||||
cache->y0 = 1;
|
||||
cache->y = 1;
|
||||
cache->x = 1;
|
||||
cache->atlases = g_ptr_array_new_with_free_func (free_atlas);
|
||||
g_ptr_array_add (cache->atlases, create_atlas ());
|
||||
}
|
||||
|
||||
static void
|
||||
@ -51,8 +77,7 @@ gsk_vulkan_glyph_cache_finalize (GObject *object)
|
||||
GskVulkanGlyphCache *cache = GSK_VULKAN_GLYPH_CACHE (object);
|
||||
|
||||
g_hash_table_unref (cache->hash_table);
|
||||
cairo_surface_destroy (cache->surface);
|
||||
g_clear_object (&cache->image);
|
||||
g_ptr_array_unref (cache->atlases);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_glyph_cache_parent_class)->finalize (object);
|
||||
}
|
||||
@ -112,21 +137,41 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
cairo_t *cr;
|
||||
cairo_scaled_font_t *scaled_font;
|
||||
cairo_glyph_t cg;
|
||||
Atlas *atlas;
|
||||
int i;
|
||||
|
||||
if (cache->x + value->draw_width + 1 >= cache->width)
|
||||
for (i = 0; i < cache->atlases->len; i++)
|
||||
{
|
||||
/* start a new row */
|
||||
cache->y0 = cache->y + 1;
|
||||
cache->x = 1;
|
||||
int x, y, y0;
|
||||
|
||||
atlas = g_ptr_array_index (cache->atlases, i);
|
||||
x = atlas->x;
|
||||
y = atlas->y;
|
||||
y0 = atlas->y0;
|
||||
|
||||
if (atlas->x + value->draw_width + 1 >= atlas->width)
|
||||
{
|
||||
/* start a new row */
|
||||
y0 = y + 1;
|
||||
x = 1;
|
||||
}
|
||||
|
||||
if (y0 + value->draw_height + 1 >= atlas->height)
|
||||
continue;
|
||||
|
||||
atlas->y0 = y0;
|
||||
atlas->x = x;
|
||||
atlas->y = y;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cache->y0 + value->draw_height + 1 >= cache->height)
|
||||
if (i == cache->atlases->len)
|
||||
{
|
||||
g_critical ("Drats! Out of cache space. We should really handle this");
|
||||
return;
|
||||
atlas = create_atlas ();
|
||||
g_ptr_array_add (cache->atlases, atlas);
|
||||
}
|
||||
|
||||
cr = cairo_create (cache->surface);
|
||||
cr = cairo_create (atlas->surface);
|
||||
|
||||
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
|
||||
if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
|
||||
@ -136,44 +181,26 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
cairo_set_source_rgba (cr, 1, 1, 1, 1);
|
||||
|
||||
cg.index = glyph;
|
||||
cg.x = cache->x - value->draw_x;
|
||||
cg.y = cache->y0 - value->draw_y;
|
||||
cg.x = atlas->x - value->draw_x;
|
||||
cg.y = atlas->y0 - value->draw_y;
|
||||
|
||||
cairo_show_glyphs (cr, &cg, 1);
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
cache->x = cache->x + value->draw_width + 1;
|
||||
cache->y = MAX (cache->y, cache->y0 + value->draw_height + 1);
|
||||
atlas->x = atlas->x + value->draw_width + 1;
|
||||
atlas->y = MAX (atlas->y, atlas->y0 + value->draw_height + 1);
|
||||
|
||||
value->tx = (cg.x + value->draw_x) / cache->width;
|
||||
value->ty = (cg.y + value->draw_y) / cache->height;
|
||||
value->tw = (float)value->draw_width / cache->width;
|
||||
value->th = (float)value->draw_height / cache->height;
|
||||
value->tx = (cg.x + value->draw_x) / atlas->width;
|
||||
value->ty = (cg.y + value->draw_y) / atlas->height;
|
||||
value->tw = (float)value->draw_width / atlas->width;
|
||||
value->th = (float)value->draw_height / atlas->height;
|
||||
|
||||
value->texture_index = 0;
|
||||
value->texture_index = i;
|
||||
|
||||
g_clear_object (&atlas->image); /* force re-upload */
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
dump_glyph_cache_stats (GskVulkanGlyphCache *cache)
|
||||
{
|
||||
static gint64 time;
|
||||
gint64 now;
|
||||
|
||||
if (!cache->hash_table)
|
||||
return;
|
||||
|
||||
now = g_get_monotonic_time ();
|
||||
if (now - time < 1000000)
|
||||
return;
|
||||
|
||||
time = now;
|
||||
|
||||
cairo_surface_write_to_png (cache->surface, "gsk-glyph-cache.png");
|
||||
}
|
||||
#endif
|
||||
|
||||
GskVulkanGlyphCache *
|
||||
gsk_vulkan_glyph_cache_new (void)
|
||||
{
|
||||
@ -199,7 +226,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
GlyphCacheKey *key;
|
||||
PangoRectangle ink_rect;
|
||||
|
||||
value = g_new (GskVulkanCachedGlyph, 1);
|
||||
value = g_new0 (GskVulkanCachedGlyph, 1);
|
||||
|
||||
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
|
||||
pango_extents_to_pixels (&ink_rect, NULL);
|
||||
@ -210,10 +237,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
value->draw_height = ink_rect.height;
|
||||
|
||||
if (ink_rect.width > 0 && ink_rect.height > 0)
|
||||
{
|
||||
add_to_cache (cache, font, glyph, value);
|
||||
g_clear_object (&cache->image);
|
||||
}
|
||||
add_to_cache (cache, font, glyph, value);
|
||||
|
||||
key = g_new (GlyphCacheKey, 1);
|
||||
key->font = g_object_ref (font);
|
||||
@ -230,12 +254,18 @@ gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
|
||||
GskVulkanUploader *uploader,
|
||||
guint index)
|
||||
{
|
||||
if (cache->image == NULL)
|
||||
cache->image = gsk_vulkan_image_new_from_data (uploader,
|
||||
cairo_image_surface_get_data (cache->surface),
|
||||
cairo_image_surface_get_width (cache->surface),
|
||||
cairo_image_surface_get_height (cache->surface),
|
||||
cairo_image_surface_get_stride (cache->surface));
|
||||
Atlas *atlas;
|
||||
|
||||
return cache->image;
|
||||
g_return_val_if_fail (index < cache->atlases->len, NULL);
|
||||
|
||||
atlas = g_ptr_array_index (cache->atlases, index);
|
||||
|
||||
if (atlas->image == NULL)
|
||||
atlas->image = gsk_vulkan_image_new_from_data (uploader,
|
||||
cairo_image_surface_get_data (atlas->surface),
|
||||
cairo_image_surface_get_width (atlas->surface),
|
||||
cairo_image_surface_get_height (atlas->surface),
|
||||
cairo_image_surface_get_stride (atlas->surface));
|
||||
|
||||
return atlas->image;
|
||||
}
|
||||
|
@ -71,9 +71,9 @@ struct _GskVulkanOpText
|
||||
gsize vertex_offset; /* offset into vertex buffer */
|
||||
gsize vertex_count; /* number of vertices */
|
||||
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
|
||||
guint texture_index;
|
||||
guint start_glyph;
|
||||
guint num_glyphs;
|
||||
guint texture_index; /* index of the texture in the glyph cache */
|
||||
guint start_glyph; /* the first glyph in nodes glyphstring that we render */
|
||||
guint num_glyphs; /* number of *non-empty* glyphs (== instances) we render */
|
||||
};
|
||||
|
||||
struct _GskVulkanOpPushConstants
|
||||
@ -217,8 +217,8 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
||||
{
|
||||
PangoFont *font = gsk_text_node_get_font (node);
|
||||
PangoGlyphString *glyphs = gsk_text_node_get_glyphs (node);
|
||||
PangoGlyph glyph;
|
||||
int i;
|
||||
guint count;
|
||||
guint texture_index;
|
||||
GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
|
||||
|
||||
@ -248,23 +248,39 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
||||
}
|
||||
op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
|
||||
|
||||
i = 0;
|
||||
texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyphs->glyphs[0].glyph);
|
||||
while (i < glyphs->num_glyphs)
|
||||
op.text.start_glyph = 0;
|
||||
op.text.texture_index = G_MAXUINT;
|
||||
|
||||
for (i = 0, count = 0; i < glyphs->num_glyphs; i++)
|
||||
{
|
||||
op.text.start_glyph = i;
|
||||
op.text.texture_index = texture_index;
|
||||
PangoGlyphInfo *gi = &glyphs->glyphs[i];
|
||||
|
||||
do {
|
||||
i++;
|
||||
glyph = glyphs->glyphs[i].glyph;
|
||||
if (glyph != PANGO_GLYPH_EMPTY && !(glyph & PANGO_GLYPH_UNKNOWN_FLAG))
|
||||
texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyph);
|
||||
} while (i < glyphs->num_glyphs && op.text.texture_index == texture_index);
|
||||
if (gi->glyph != PANGO_GLYPH_EMPTY && !(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
|
||||
{
|
||||
texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, gi->glyph);
|
||||
if (op.text.texture_index == G_MAXUINT)
|
||||
op.text.texture_index = texture_index;
|
||||
if (texture_index != op.text.texture_index)
|
||||
{
|
||||
op.text.num_glyphs = count;
|
||||
|
||||
op.text.num_glyphs = i - op.text.start_glyph;
|
||||
g_array_append_val (self->render_ops, op);
|
||||
|
||||
count = 1;
|
||||
op.text.start_glyph = i;
|
||||
op.text.texture_index = texture_index;
|
||||
}
|
||||
else
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (op.text.texture_index != G_MAXUINT && count != 0)
|
||||
{
|
||||
op.text.num_glyphs = count;
|
||||
g_array_append_val (self->render_ops, op);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -112,14 +112,14 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
|
||||
guint num_glyphs)
|
||||
{
|
||||
GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
|
||||
int i
|
||||
int i;
|
||||
int count = 0;
|
||||
int x_position = 0;
|
||||
|
||||
for (i = 0; i < start_glyph; i++)
|
||||
x_position += glyphs->glyphs[i].geometry.width;
|
||||
|
||||
for (; i < num_glyphs; i++)
|
||||
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
|
||||
{
|
||||
PangoGlyphInfo *gi = &glyphs->glyphs[i];
|
||||
|
||||
@ -134,6 +134,7 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
|
||||
GskVulkanCachedGlyph *glyph;
|
||||
|
||||
glyph = gsk_vulkan_renderer_get_cached_glyph (renderer, font, gi->glyph);
|
||||
|
||||
instance->tex_rect[0] = glyph->tx;
|
||||
instance->tex_rect[1] = glyph->ty;
|
||||
instance->tex_rect[2] = glyph->tw;
|
||||
|
Loading…
Reference in New Issue
Block a user