Compare commits

...

12 Commits

Author SHA1 Message Date
Matthias Clasen
3a6dd85e7f gsk: Fix serialization of text nodes 2017-09-20 21:08:36 -04:00
Matthias Clasen
25cd8230f3 Add simple aging for the glyph cache
This should prevent the cache from growing out of bounds.
2017-09-20 19:18:58 -04:00
Lionel Landwerlin
066d9db434 gsk vulkan: Use new api in the glyph cache
Use the newly introduced gsk_vulkan_image_new_for_atlas.
2017-09-20 19:13:46 -04:00
Lionel Landwerlin
d233142d0b gsk: vulkan: rework image layout/access transitions
By tracking the last transition we can build the appropriate barriers.
Also use the most appropriate initial layout/access at creation :

for linear image : predefined (we prepare the content ourself through memcpy)
for everything else : undefined (we don't care about the content, will most likely be erase)
2017-09-20 19:13:46 -04:00
Lionel Landwerlin
e911c82013 gsk: vulkan: silence anisotropy validation warning 2017-09-20 19:13:46 -04:00
Matthias Clasen
aa309fbd63 glyph cache: Upload glyphs incrementally
This does not work yet.
2017-09-20 19:13:46 -04:00
Matthias Clasen
5b8588e413 vulkan: pass the context when creating a glyph cache 2017-09-20 10:06:29 -04:00
Matthias Clasen
3cd27dc3d1 vulkan: Add an upload_region api to GskVulkanImage
This will let us update larger textures incrementally.
Sadly, it does not work yet.
2017-09-20 10:00:48 -04:00
Matthias Clasen
8daf9fb703 Add some debug output for the glyph cache
Print out some statistics and dump the glyph caches to a png,
for now.
2017-09-20 10:00:18 -04:00
Matthias Clasen
52db607952 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.
2017-09-20 10:00:17 -04:00
Matthias Clasen
3e955556b2 vulkan: Move glyph cache to a separate file
Otherwise things will get too messy.
2017-09-19 19:38:32 -04:00
Matthias Clasen
4e6560a522 vulkan: Better glyph cache api
Move the glyph caching api to something that can support using
multiple textures. We now split the text render ops into multiple
ops for different textures, and make each op render just a substring
of the text node's glyph string.
2017-09-19 19:38:32 -04:00
15 changed files with 952 additions and 553 deletions

View File

@@ -10,7 +10,8 @@ static const GDebugKey gsk_debug_keys[] = {
{ "transforms", GSK_DEBUG_TRANSFORMS },
{ "surface", GSK_DEBUG_SURFACE },
{ "vulkan", GSK_DEBUG_VULKAN },
{ "fallback", GSK_DEBUG_FALLBACK }
{ "fallback", GSK_DEBUG_FALLBACK },
{ "glyphcache", GSK_DEBUG_GLYPH_CACHE }
};
#endif

View File

@@ -14,7 +14,8 @@ typedef enum {
GSK_DEBUG_TRANSFORMS = 1 << 5,
GSK_DEBUG_SURFACE = 1 << 6,
GSK_DEBUG_VULKAN = 1 << 7,
GSK_DEBUG_FALLBACK = 1 << 8
GSK_DEBUG_FALLBACK = 1 << 8,
GSK_DEBUG_GLYPH_CACHE = 1 << 9
} GskDebugFlags;
#define GSK_DEBUG_ANY ((1 << 9) - 1)

View File

@@ -3901,6 +3901,8 @@ gsk_text_node_serialize (GskRenderNode *node)
desc = pango_font_describe (self->font);
s = pango_font_description_to_string (desc);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uiiii)"));
for (i = 0; i < self->glyphs->num_glyphs; i++)
{
PangoGlyphInfo *glyph = &self->glyphs->glyphs[i];
@@ -3912,7 +3914,6 @@ gsk_text_node_serialize (GskRenderNode *node)
glyph->attr.is_cluster_start);
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uiiii)"));
v = g_variant_new (GSK_TEXT_NODE_VARIANT_TYPE,
s,
self->color.red,
@@ -3935,7 +3936,7 @@ gsk_text_node_deserialize (GVariant *variant,
{
PangoFont *font;
PangoGlyphString *glyphs;
GVariantIter iter;
GVariantIter *iter;
GskRenderNode *result;
PangoGlyphInfo glyph;
PangoFontDescription *desc;
@@ -3950,7 +3951,7 @@ gsk_text_node_deserialize (GVariant *variant,
if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error))
return NULL;
g_variant_get (variant, "(&sddddiidda(uiiii))",
g_variant_get (variant, "(&sdddddda(uiiii))",
&s, &color.red, &color.green, &color.blue, &color.alpha,
&x, &y, &iter);
@@ -3960,9 +3961,9 @@ gsk_text_node_deserialize (GVariant *variant,
font = pango_font_map_load_font (fontmap, context, desc);
glyphs = pango_glyph_string_new ();
pango_glyph_string_set_size (glyphs, g_variant_iter_n_children (&iter));
pango_glyph_string_set_size (glyphs, g_variant_iter_n_children (iter));
i = 0;
while (g_variant_iter_next (&iter, "(uiiii)",
while (g_variant_iter_next (iter, "(uiiii)",
&glyph.glyph, &glyph.geometry.width,
&glyph.geometry.x_offset, &glyph.geometry.y_offset,
&cluster_start))
@@ -3971,6 +3972,7 @@ gsk_text_node_deserialize (GVariant *variant,
glyphs->glyphs[i] = glyph;
i++;
}
g_variant_iter_free (iter);
result = gsk_text_node_new (font, glyphs, &color, x, y); /* FIXME: Avoid copying glyphs */

View File

@@ -99,15 +99,19 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
PangoFont *font,
PangoGlyphString *glyphs,
float x,
float y)
float y,
guint start_glyph,
guint num_glyphs)
{
GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
int i, count;
int i;
int count = 0;
int x_position = 0;
float dx, dy, dw, dh;
count = 0;
for (i = 0; i < glyphs->num_glyphs; i++)
for (i = 0; i < start_glyph; i++)
x_position += glyphs->glyphs[i].geometry.width;
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
{
PangoGlyphInfo *gi = &glyphs->glyphs[i];
@@ -119,17 +123,19 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
{
GskVulkanColorTextInstance *instance = &instances[count];
GskVulkanCachedGlyph *glyph;
gsk_vulkan_renderer_get_glyph_coords (renderer, font, gi->glyph,
&instance->tex_rect[0],
&instance->tex_rect[1],
&instance->tex_rect[2],
&instance->tex_rect[3],
&dx, &dy, &dw, &dh);
instance->rect[0] = x + cx + dx;
instance->rect[1] = y + cy + dy;
instance->rect[2] = dw;
instance->rect[3] = dh;
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;
instance->tex_rect[3] = glyph->th;
instance->rect[0] = x + cx + glyph->draw_x;
instance->rect[1] = y + cy + glyph->draw_y;
instance->rect[2] = glyph->draw_width;
instance->rect[3] = glyph->draw_height;
count++;
}

View File

@@ -27,7 +27,9 @@ void gsk_vulkan_color_text_pipeline_collect_vertex_data (Gs
PangoFont *font,
PangoGlyphString *glyphs,
float x,
float y);
float y,
guint start_glyph,
guint num_glyphs);
gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
VkCommandBuffer command_buffer,
gsize offset,

448
gsk/gskvulkanglyphcache.c Normal file
View File

@@ -0,0 +1,448 @@
#include "config.h"
#include "gskvulkanglyphcacheprivate.h"
#include "gskvulkanimageprivate.h"
#include "gskdebugprivate.h"
#include "gskprivate.h"
#include <graphene.h>
/* Parameters for our 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, then
* we drop the atlas an all the glyphs contained in it from the cache.
*/
#define MAX_AGE 60
#define CHECK_INTERVAL 10
#define MAX_OLD 0.333
typedef struct {
cairo_surface_t *surface;
GskVulkanImage *image;
int width, height;
int x, y, y0;
int num_glyphs;
GList *dirty_glyphs;
guint old_pixels;
} Atlas;
struct _GskVulkanGlyphCache {
GObject parent_instance;
GdkVulkanContext *vulkan;
GHashTable *hash_table;
GPtrArray *atlases;
guint64 timestamp;
};
struct _GskVulkanGlyphCacheClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE (GskVulkanGlyphCache, gsk_vulkan_glyph_cache, G_TYPE_OBJECT)
static guint glyph_cache_hash (gconstpointer v);
static gboolean glyph_cache_equal (gconstpointer v1,
gconstpointer v2);
static void glyph_cache_key_free (gpointer v);
static void glyph_cache_value_free (gpointer v);
static Atlas *
create_atlas (GskVulkanGlyphCache *cache)
{
Atlas *atlas;
atlas = g_new0 (Atlas, 1);
atlas->width = 512;
atlas->height = 512;
atlas->y0 = 1;
atlas->y = 1;
atlas->x = 1;
atlas->image = NULL;
atlas->num_glyphs = 0;
atlas->dirty_glyphs = 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_list_free_full (atlas->dirty_glyphs, g_free);
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->atlases = g_ptr_array_new_with_free_func (free_atlas);
}
static void
gsk_vulkan_glyph_cache_finalize (GObject *object)
{
GskVulkanGlyphCache *cache = GSK_VULKAN_GLYPH_CACHE (object);
g_ptr_array_unref (cache->atlases);
g_hash_table_unref (cache->hash_table);
G_OBJECT_CLASS (gsk_vulkan_glyph_cache_parent_class)->finalize (object);
}
static void
gsk_vulkan_glyph_cache_class_init (GskVulkanGlyphCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsk_vulkan_glyph_cache_finalize;
}
typedef struct {
PangoFont *font;
PangoGlyph glyph;
} GlyphCacheKey;
static gboolean
glyph_cache_equal (gconstpointer v1, gconstpointer v2)
{
const GlyphCacheKey *key1 = v1;
const GlyphCacheKey *key2 = v2;
return key1->font == key2->font &&
key1->glyph == key2->glyph;
}
static guint
glyph_cache_hash (gconstpointer v)
{
const GlyphCacheKey *key = v;
return GPOINTER_TO_UINT (key->font) ^ key->glyph;
}
static void
glyph_cache_key_free (gpointer v)
{
GlyphCacheKey *f = v;
g_object_unref (f->font);
g_free (f);
}
static void
glyph_cache_value_free (gpointer v)
{
g_free (v);
}
typedef struct {
GlyphCacheKey *key;
GskVulkanCachedGlyph *value;
} DirtyGlyph;
static void
add_to_cache (GskVulkanGlyphCache *cache,
GlyphCacheKey *key,
GskVulkanCachedGlyph *value)
{
Atlas *atlas;
int i;
DirtyGlyph *dirty;
for (i = 0; i < cache->atlases->len; i++)
{
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 (i == cache->atlases->len)
{
atlas = create_atlas (cache);
g_ptr_array_add (cache->atlases, atlas);
}
value->tx = (float)atlas->x / atlas->width;
value->ty = (float)atlas->y0 / atlas->height;
value->tw = (float)value->draw_width / atlas->width;
value->th = (float)value->draw_height / atlas->height;
value->texture_index = i;
dirty = g_new (DirtyGlyph, 1);
dirty->key = key;
dirty->value = value;
atlas->dirty_glyphs = g_list_prepend (atlas->dirty_glyphs, dirty);
atlas->x = atlas->x + value->draw_width + 1;
atlas->y = MAX (atlas->y, atlas->y0 + value->draw_height + 1);
atlas->num_glyphs++;
#ifdef G_ENABLE_DEBUG
if (GSK_DEBUG_CHECK(GLYPH_CACHE))
{
g_print ("Glyph cache:\n");
for (i = 0; i < cache->atlases->len; i++)
{
atlas = g_ptr_array_index (cache->atlases, i);
g_print ("\tAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
i, atlas->width, atlas->height,
atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
atlas->x, atlas->y0, atlas->y);
}
}
#endif
}
static void
upload_glyph (Atlas *atlas,
GskVulkanUploader *uploader,
GlyphCacheKey *key,
GskVulkanCachedGlyph *value)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
cairo_glyph_t cg;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
value->draw_width,
value->draw_height);
cr = cairo_create (surface);
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;
cairo_set_scaled_font (cr, scaled_font);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cg.index = key->glyph;
cg.x = - value->draw_x;
cg.y = - value->draw_y;
cairo_show_glyphs (cr, &cg, 1);
cairo_destroy (cr);
gsk_vulkan_image_upload_region (atlas->image,
uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
cairo_image_surface_get_stride (surface),
(gsize)(value->tx * atlas->width),
(gsize)(value->ty * atlas->height));
cairo_surface_destroy (surface);
}
GskVulkanGlyphCache *
gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan)
{
GskVulkanGlyphCache *cache;
cache = GSK_VULKAN_GLYPH_CACHE (g_object_new (GSK_TYPE_VULKAN_GLYPH_CACHE, NULL));
cache->vulkan = vulkan;
g_ptr_array_add (cache->atlases, create_atlas (cache));
return cache;
}
GskVulkanCachedGlyph *
gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph)
{
GlyphCacheKey lookup_key;
GskVulkanCachedGlyph *value;
lookup_key.font = font;
lookup_key.glyph = glyph;
value = g_hash_table_lookup (cache->hash_table, &lookup_key);
if (value)
{
if (cache->timestamp - value->timestamp >= MAX_AGE)
{
Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
atlas->old_pixels -= value->draw_width * value->draw_height;
value->timestamp = cache->timestamp;
}
}
if (create && value == NULL)
{
GlyphCacheKey *key;
PangoRectangle ink_rect;
key = g_new (GlyphCacheKey, 1);
value = g_new0 (GskVulkanCachedGlyph, 1);
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL);
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;
key->font = g_object_ref (font);
key->glyph = glyph;
if (ink_rect.width > 0 && ink_rect.height > 0)
add_to_cache (cache, key, value);
g_hash_table_insert (cache->hash_table, key, value);
}
return value;
}
GskVulkanImage *
gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
GskVulkanUploader *uploader,
guint index)
{
Atlas *atlas;
GList *l;
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_for_atlas (cache->vulkan, atlas->width, atlas->height);
for (l = atlas->dirty_glyphs; l; l = l->next)
{
DirtyGlyph *glyph = l->data;
upload_glyph (atlas, uploader, glyph->key, glyph->value);
}
g_list_free_full (atlas->dirty_glyphs, g_free);
atlas->dirty_glyphs = NULL;
return atlas->image;
}
void
gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
{
int i, j;
guint *drops;
guint *shifts;
guint len;
GHashTableIter iter;
GlyphCacheKey *key;
GskVulkanCachedGlyph *value;
guint dropped = 0;
cache->timestamp++;
if (cache->timestamp % CHECK_INTERVAL != 0)
return;
len = cache->atlases->len;
/* look for glyphs that have grown old since last time */
g_hash_table_iter_init (&iter, cache->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
guint age;
age = cache->timestamp - value->timestamp;
if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
{
Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
atlas->old_pixels += value->draw_width * value->draw_height;
}
}
drops = g_alloca (sizeof (guint) * len);
shifts = g_alloca (sizeof (guint) * len);
for (i = 0; i < len; i++)
{
drops[i] = 0;
shifts[i] = i;
}
/* look for atlases to drop, and create a mapping of updated texture indices */
for (i = cache->atlases->len - 1; i >= 0; i--)
{
Atlas *atlas = g_ptr_array_index (cache->atlases, i);
if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
{
GSK_NOTE(GLYPH_CACHE,
g_print ("Dropping atlas %d (%g.2%% old)\n", i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height)));
g_ptr_array_remove_index (cache->atlases, i);
drops[i] = 1;
for (j = i; j + 1 < len; j++)
shifts[j + 1] = shifts[j];
}
}
/* no atlas dropped, we're done */
if (len == cache->atlases->len)
return;
/* purge glyphs and update texture indices */
g_hash_table_iter_init (&iter, cache->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
if (drops[value->texture_index])
{
dropped++;
g_hash_table_iter_remove (&iter);
}
else
{
value->texture_index = shifts[value->texture_index];
}
}
GSK_NOTE(GLYPH_CACHE, g_print ("Dropped %d glyphs\n", dropped));
}

View File

@@ -0,0 +1,27 @@
#ifndef __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__
#define __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__
#include <pango/pango.h>
#include "gskvulkanrendererprivate.h"
#include "gskvulkanimageprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_VULKAN_GLYPH_CACHE (gsk_vulkan_glyph_cache_get_type ())
G_DECLARE_FINAL_TYPE(GskVulkanGlyphCache, gsk_vulkan_glyph_cache, GSK, VULKAN_GLYPH_CACHE, GObject)
GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan);
GskVulkanImage * gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
GskVulkanUploader *uploader,
guint index);
GskVulkanCachedGlyph *gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph);
void gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache);
#endif /* __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__ */

View File

@@ -14,9 +14,11 @@ struct _GskVulkanUploader
GskVulkanCommandPool *command_pool;
GArray *before_barriers;
GArray *before_buffer_barriers;
GArray *before_image_barriers;
VkCommandBuffer copy_buffer;
GArray *after_barriers;
GArray *after_buffer_barriers;
GArray *after_image_barriers;
GSList *staging_image_free_list;
GSList *staging_buffer_free_list;
@@ -30,8 +32,11 @@ struct _GskVulkanImage
gsize width;
gsize height;
VkImageUsageFlags vk_usage;
VkImage vk_image;
VkImageView vk_image_view;
VkImageLayout vk_image_layout;
VkAccessFlags vk_access;
GskVulkanMemory *memory;
};
@@ -49,8 +54,11 @@ gsk_vulkan_uploader_new (GdkVulkanContext *context,
self->vulkan = g_object_ref (context);
self->command_pool = command_pool;
self->before_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
self->after_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
self->before_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
self->after_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
self->before_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
self->after_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
return self;
}
@@ -60,8 +68,10 @@ gsk_vulkan_uploader_free (GskVulkanUploader *self)
{
gsk_vulkan_uploader_reset (self);
g_array_unref (self->after_barriers);
g_array_unref (self->before_barriers);
g_array_unref (self->after_buffer_barriers);
g_array_unref (self->before_buffer_barriers);
g_array_unref (self->after_image_barriers);
g_array_unref (self->before_image_barriers);
g_object_unref (self->vulkan);
@@ -71,14 +81,51 @@ gsk_vulkan_uploader_free (GskVulkanUploader *self)
static void
gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self,
gboolean after,
const VkImageMemoryBarrier *barrier)
GskVulkanImage *image,
VkImageLayout new_layout,
VkAccessFlags new_access)
{
GArray *array;
VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = image->vk_access,
.dstAccessMask = new_access,
.oldLayout = image->vk_image_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};
if (after)
array = self->after_image_barriers;
else
array = self->before_image_barriers;
g_array_append_val (array, barrier);
image->vk_image_layout = new_layout;
image->vk_access = new_access;
}
static void
gsk_vulkan_uploader_add_buffer_barrier (GskVulkanUploader *self,
gboolean after,
const VkBufferMemoryBarrier *barrier)
{
GArray *array;
if (after)
array = self->after_barriers;
array = self->after_buffer_barriers;
else
array = self->before_barriers;
array = self->before_buffer_barriers;
g_array_append_val (array, *barrier);
}
@@ -95,7 +142,7 @@ gsk_vulkan_uploader_get_copy_buffer (GskVulkanUploader *self)
void
gsk_vulkan_uploader_upload (GskVulkanUploader *self)
{
if (self->before_barriers->len > 0)
if (self->before_buffer_barriers->len > 0 || self->before_image_barriers->len > 0)
{
VkCommandBuffer command_buffer;
@@ -105,14 +152,15 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0,
0, NULL,
0, NULL,
self->before_barriers->len, (VkImageMemoryBarrier *) self->before_barriers->data);
self->before_buffer_barriers->len, (VkBufferMemoryBarrier *) self->before_buffer_barriers->data,
self->before_image_barriers->len, (VkImageMemoryBarrier *) self->before_image_barriers->data);
gsk_vulkan_command_pool_submit_buffer (self->command_pool, command_buffer, VK_NULL_HANDLE);
g_array_set_size (self->before_barriers, 0);
g_array_set_size (self->before_buffer_barriers, 0);
g_array_set_size (self->before_image_barriers, 0);
}
/* append these to existing buffer */
if (self->after_barriers->len > 0)
if (self->after_buffer_barriers->len > 0 || self->after_image_barriers->len > 0)
{
VkCommandBuffer command_buffer = gsk_vulkan_uploader_get_copy_buffer (self);
vkCmdPipelineBarrier (command_buffer,
@@ -120,9 +168,10 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0,
0, NULL,
0, NULL,
self->after_barriers->len, (VkImageMemoryBarrier *) self->after_barriers->data);
g_array_set_size (self->after_barriers, 0);
self->after_buffer_barriers->len, (VkBufferMemoryBarrier *) self->after_buffer_barriers->data,
self->after_image_barriers->len, (VkImageMemoryBarrier *) self->after_image_barriers->data);
g_array_set_size (self->after_buffer_barriers, 0);
g_array_set_size (self->after_image_barriers, 0);
}
if (self->copy_buffer != VK_NULL_HANDLE)
@@ -135,9 +184,9 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
void
gsk_vulkan_uploader_reset (GskVulkanUploader *self)
{
g_array_set_size (self->before_barriers, 0);
g_array_set_size (self->before_image_barriers, 0);
self->copy_buffer = VK_NULL_HANDLE;
g_array_set_size (self->after_barriers, 0);
g_array_set_size (self->after_image_barriers, 0);
g_slist_free_full (self->staging_image_free_list, g_object_unref);
self->staging_image_free_list = NULL;
@@ -151,6 +200,8 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
gsize height,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageLayout layout,
VkAccessFlags access,
VkMemoryPropertyFlags memory)
{
VkMemoryRequirements requirements;
@@ -161,6 +212,9 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
self->vulkan = g_object_ref (context);
self->width = width;
self->height = height;
self->vk_usage = usage;
self->vk_image_layout = layout;
self->vk_access = access;
GSK_VK_CHECK (vkCreateImage, gdk_vulkan_context_get_device (context),
&(VkImageCreateInfo) {
@@ -175,7 +229,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
.tiling = tiling,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
.initialLayout = self->vk_image_layout,
},
NULL,
&self->vk_image);
@@ -270,9 +324,10 @@ gsk_vulkan_image_new_from_data_via_staging_buffer (GskVulkanUploader *uploader,
{
GskVulkanImage *self;
GskVulkanBuffer *staging;
gsize buffer_size = width * height * 4;
guchar *mem;
staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, width * height * 4);
staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, buffer_size);
mem = gsk_vulkan_buffer_map (staging);
if (stride == width * 4)
@@ -289,32 +344,34 @@ gsk_vulkan_image_new_from_data_via_staging_buffer (GskVulkanUploader *uploader,
gsk_vulkan_buffer_unmap (staging);
gsk_vulkan_uploader_add_buffer_barrier (uploader,
FALSE,
&(VkBufferMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = gsk_vulkan_buffer_get_buffer(staging),
.offset = 0,
.size = buffer_size,
});
self = gsk_vulkan_image_new (uploader->vulkan,
width,
height,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT);
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
gsk_vulkan_buffer_get_buffer (staging),
@@ -341,23 +398,9 @@ gsk_vulkan_image_new_from_data_via_staging_buffer (GskVulkanUploader *uploader,
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging);
@@ -377,58 +420,37 @@ gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
staging = gsk_vulkan_image_new (uploader->vulkan,
width,
height,
height,
VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
gsk_vulkan_image_upload_data (staging, data, width, height, stride);
self = gsk_vulkan_image_new (uploader->vulkan,
width,
height,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = staging->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
staging,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT);
vkCmdCopyImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
staging->vk_image,
@@ -451,7 +473,7 @@ gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
.layerCount = 1
},
.dstOffset = { 0, 0, 0 },
.extent = {
.extent = {
.width = width,
.height = height,
.depth = 1
@@ -460,23 +482,9 @@ gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
uploader->staging_image_free_list = g_slist_prepend (uploader->staging_image_free_list, staging);
@@ -496,32 +504,20 @@ gsk_vulkan_image_new_from_data_directly (GskVulkanUploader *uploader,
self = gsk_vulkan_image_new (uploader->vulkan,
width,
height,
height,
VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_ACCESS_HOST_WRITE_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
gsk_vulkan_image_upload_data (self, data, width, height, stride);
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
@@ -574,9 +570,32 @@ gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
self = gsk_vulkan_image_new (context,
width,
height,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
return self;
}
GskVulkanImage *
gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
gsize width,
gsize height)
{
GskVulkanImage *self;
self = gsk_vulkan_image_new (context,
width,
height,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
0,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
@@ -594,23 +613,9 @@ gsk_vulkan_image_download (GskVulkanImage *self,
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
&(VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = self->vk_image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
});
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
buffer = gsk_vulkan_buffer_new_download (self->vulkan, self->width * self->height * 4);
@@ -649,6 +654,76 @@ gsk_vulkan_image_download (GskVulkanImage *self,
return texture;
}
void
gsk_vulkan_image_upload_region (GskVulkanImage *self,
GskVulkanUploader *uploader,
guchar *data,
gsize width,
gsize height,
gsize stride,
gsize x,
gsize y)
{
GskVulkanBuffer *staging;
guchar *mem;
staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, width * height * 4);
mem = gsk_vulkan_buffer_map (staging);
if (stride == width * 4)
{
memcpy (mem, data, stride * height);
}
else
{
for (gsize i = 0; i < height; i++)
{
memcpy (mem + i * width * 4, data + i * stride, width * 4);
}
}
gsk_vulkan_buffer_unmap (staging);
gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE,
self,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_ACCESS_TRANSFER_WRITE_BIT);
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
gsk_vulkan_buffer_get_buffer (staging),
self->vk_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = { x, y, 0 },
.imageExtent = {
.width = width,
.height = height,
.depth = 1
}
}
});
gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE,
self,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT);
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
}
static void
gsk_vulkan_image_finalize (GObject *object)
{
@@ -711,4 +786,3 @@ gsk_vulkan_image_get_image_view (GskVulkanImage *self)
{
return self->vk_image_view;
}

View File

@@ -31,9 +31,20 @@ GskVulkanImage * gsk_vulkan_image_new_from_data (GskVulk
gsize width,
gsize height,
gsize stride);
void gsk_vulkan_image_upload_region (GskVulkanImage *image,
GskVulkanUploader *uploader,
guchar *data,
gsize width,
gsize height,
gsize stride,
gsize x,
gsize y);
GskVulkanImage * gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
gsize width,
gsize height);
GskVulkanImage * gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
gsize width,
gsize height);
GskTexture * gsk_vulkan_image_download (GskVulkanImage *self,
GskVulkanUploader *uploader);

View File

@@ -11,6 +11,7 @@
#include "gskvulkanimageprivate.h"
#include "gskvulkanpipelineprivate.h"
#include "gskvulkanrenderprivate.h"
#include "gskvulkanglyphcacheprivate.h"
#include <graphene.h>
@@ -29,23 +30,6 @@ typedef struct {
} ProfileTimers;
#endif
typedef struct _GlyphCache GlyphCache;
struct _GlyphCache {
GHashTable *hash_table;
cairo_surface_t *surface;
int width, height;
int x, y, y0;
GskVulkanImage *image;
};
static GlyphCache *create_glyph_cache (void);
static void free_glyph_cache (GlyphCache *cache);
static void dump_glyph_cache_stats (GlyphCache *cache);
struct _GskVulkanRenderer
{
GskRenderer parent_instance;
@@ -61,7 +45,7 @@ struct _GskVulkanRenderer
GSList *textures;
GlyphCache *glyph_cache;
GskVulkanGlyphCache *glyph_cache;
#ifdef G_ENABLE_DEBUG
ProfileTimers profile_timers;
@@ -140,7 +124,8 @@ gsk_vulkan_renderer_realize (GskRenderer *renderer,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
.unnormalizedCoordinates = VK_FALSE
.unnormalizedCoordinates = VK_FALSE,
.maxAnisotropy = 1.0,
},
NULL,
&self->sampler);
@@ -153,7 +138,7 @@ gsk_vulkan_renderer_realize (GskRenderer *renderer,
self->render = gsk_vulkan_render_new (renderer, self->vulkan);
self->glyph_cache = create_glyph_cache ();
self->glyph_cache = gsk_vulkan_glyph_cache_new (self->vulkan);
return TRUE;
}
@@ -165,8 +150,7 @@ gsk_vulkan_renderer_unrealize (GskRenderer *renderer)
VkDevice device;
GSList *l;
free_glyph_cache (self->glyph_cache);
self->glyph_cache = NULL;
g_clear_object (&self->glyph_cache);
for (l = self->textures; l; l = l->next)
{
@@ -283,7 +267,11 @@ gsk_vulkan_renderer_begin_draw_frame (GskRenderer *renderer,
GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
GdkDrawingContext *result;
gsk_vulkan_glyph_cache_begin_frame (self->glyph_cache);
#if 0
GSK_NOTE(RENDERER, dump_glyph_cache_stats (self->glyph_cache));
#endif
result = gdk_window_begin_draw_frame (gsk_renderer_get_window (renderer),
GDK_DRAW_CONTEXT (self->vulkan),
@@ -370,266 +358,26 @@ gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
return image;
}
guint
gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *self,
PangoFont *font,
PangoGlyph glyph)
{
return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, TRUE, font, glyph)->texture_index;
}
GskVulkanImage *
gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
GskVulkanUploader *uploader,
PangoFont *font,
PangoGlyphString *glyphs)
guint index)
{
if (self->glyph_cache->image == NULL)
self->glyph_cache->image = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (self->glyph_cache->surface),
cairo_image_surface_get_width (self->glyph_cache->surface),
cairo_image_surface_get_height (self->glyph_cache->surface),
cairo_image_surface_get_stride (self->glyph_cache->surface));
return g_object_ref (self->glyph_cache->image);
return g_object_ref (gsk_vulkan_glyph_cache_get_glyph_image (self->glyph_cache, uploader, index));
}
typedef struct _GlyphCacheKey GlyphCacheKey;
typedef struct _GlyphCacheValue GlyphCacheValue;
struct _GlyphCacheKey {
PangoFont *font;
PangoGlyph glyph;
};
struct _GlyphCacheValue {
float tx;
float ty;
float tw;
float th;
float draw_x;
float draw_y;
float draw_width;
float draw_height;
};
static GlyphCacheValue *glyph_cache_lookup (GlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph);
void
gsk_vulkan_renderer_get_glyph_coords (GskVulkanRenderer *self,
GskVulkanCachedGlyph *
gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
PangoFont *font,
PangoGlyph glyph,
float *tx,
float *ty,
float *tw,
float *th,
float *dx,
float *dy,
float *dw,
float *dh)
PangoGlyph glyph)
{
GlyphCacheValue *gv;
gv = glyph_cache_lookup (self->glyph_cache, FALSE, font, glyph);
if (gv)
{
*tx = gv->tx;
*ty = gv->ty;
*tw = gv->tw;
*th = gv->th;
*dx = gv->draw_x;
*dy = gv->draw_y;
*dw = gv->draw_width;
*dh = gv->draw_height;
}
}
/*** Glyph cache ***/
static gboolean
glyph_cache_equal (gconstpointer v1, gconstpointer v2)
{
const GlyphCacheKey *key1 = v1;
const GlyphCacheKey *key2 = v2;
return key1->font == key2->font &&
key1->glyph == key2->glyph;
}
static guint
glyph_cache_hash (gconstpointer v)
{
const GlyphCacheKey *key = v;
return GPOINTER_TO_UINT (key->font) ^ key->glyph;
}
static void
glyph_cache_key_free (gpointer v)
{
GlyphCacheKey *f = v;
g_object_unref (f->font);
g_free (f);
}
static void
glyph_cache_value_free (gpointer v)
{
g_free (v);
}
static void
add_to_cache (GlyphCache *cache,
PangoFont *font,
PangoGlyph glyph,
GlyphCacheValue *value)
{
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
cairo_glyph_t cg;
if (cache->x + value->draw_width + 1 >= cache->width)
{
/* start a new row */
cache->y0 = cache->y + 1;
cache->x = 1;
}
if (cache->y0 + value->draw_height + 1 >= cache->height)
{
g_critical ("Drats! Out of cache space. We should really handle this");
return;
}
cr = cairo_create (cache->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))
return;
cairo_set_scaled_font (cr, scaled_font);
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;
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);
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;
}
static GlyphCacheValue *
glyph_cache_lookup (GlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph)
{
GlyphCacheKey lookup_key;
GlyphCacheValue *value;
lookup_key.font = font;
lookup_key.glyph = glyph;
value = g_hash_table_lookup (cache->hash_table, &lookup_key);
if (create && value == NULL)
{
GlyphCacheKey *key;
PangoRectangle ink_rect;
value = g_new (GlyphCacheValue, 1);
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL);
value->draw_x = ink_rect.x;
value->draw_y = ink_rect.y;
value->draw_width = ink_rect.width;
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);
}
key = g_new (GlyphCacheKey, 1);
key->font = g_object_ref (font);
key->glyph = glyph;
g_hash_table_insert (cache->hash_table, key, value);
}
return value;
}
void
gsk_vulkan_renderer_cache_glyphs (GskVulkanRenderer *self,
PangoFont *font,
PangoGlyphString *glyphs)
{
int i;
for (i = 0; i < glyphs->num_glyphs; i++)
{
PangoGlyphInfo *gi = &glyphs->glyphs[i];
if (gi->glyph != PANGO_GLYPH_EMPTY)
{
if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
(void) glyph_cache_lookup (self->glyph_cache, TRUE, font, gi->glyph);
}
}
}
static GlyphCache *
create_glyph_cache (void)
{
GlyphCache *cache;
cache = g_new0 (GlyphCache, 1);
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;
return cache;
}
static void
free_glyph_cache (GlyphCache *cache)
{
g_hash_table_unref (cache->hash_table);
cairo_surface_destroy (cache->surface);
g_free (cache);
}
static void
dump_glyph_cache_stats (GlyphCache *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");
return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, FALSE, font, glyph);
}

View File

@@ -25,26 +25,35 @@ GskVulkanImage * gsk_vulkan_renderer_ref_texture_image (GskVulk
GskTexture *texture,
GskVulkanUploader *uploader);
GskVulkanImage * gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
GskVulkanUploader *uploader,
PangoFont *font,
PangoGlyphString *glyphs);
typedef struct
{
guint texture_index;
void gsk_vulkan_renderer_get_glyph_coords (GskVulkanRenderer *self,
PangoFont *font,
PangoGlyph glyph,
float *tx,
float *ty,
float *tw,
float *th,
float *dx,
float *dy,
float *dw,
float *dh);
float tx;
float ty;
float tw;
float th;
int draw_x;
int draw_y;
int draw_width;
int draw_height;
guint64 timestamp;
} GskVulkanCachedGlyph;
guint gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *renderer,
PangoFont *font,
PangoGlyph glyph);
GskVulkanImage * gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
GskVulkanUploader *uploader,
guint index);
GskVulkanCachedGlyph * gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
PangoFont *font,
PangoGlyph glyph);
void gsk_vulkan_renderer_cache_glyphs (GskVulkanRenderer *renderer,
PangoFont *font,
PangoGlyphString *glyphs);
G_END_DECLS

View File

@@ -25,6 +25,7 @@
typedef union _GskVulkanOp GskVulkanOp;
typedef struct _GskVulkanOpRender GskVulkanOpRender;
typedef struct _GskVulkanOpText GskVulkanOpText;
typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants;
typedef enum {
@@ -60,6 +61,21 @@ struct _GskVulkanOpRender
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
};
struct _GskVulkanOpText
{
GskVulkanOpType type;
GskRenderNode *node; /* node that's the source of this op */
GskVulkanPipeline *pipeline; /* pipeline to use */
GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
GskVulkanImage *source; /* source image to render */
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; /* 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
{
GskVulkanOpType type;
@@ -72,6 +88,7 @@ union _GskVulkanOp
GskVulkanOpType type;
GskVulkanOpRender render;
GskVulkanOpPushConstants constants;
GskVulkanOpText text;
};
struct _GskVulkanRenderPass
@@ -197,37 +214,75 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
return;
case GSK_TEXT_NODE:
gsk_vulkan_renderer_cache_glyphs (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
gsk_text_node_get_font (node),
gsk_text_node_get_glyphs (node));
{
PangoFont *font = gsk_text_node_get_font (node);
PangoGlyphString *glyphs = gsk_text_node_get_glyphs (node);
int i;
guint count;
guint texture_index;
GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
if (font_has_color_glyphs (gsk_text_node_get_font (node)))
{
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_COLOR_TEXT;
}
else
{
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_TEXT;
}
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return;
if (font_has_color_glyphs (font))
{
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_COLOR_TEXT;
}
else
{
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_TEXT;
}
op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
op.text.start_glyph = 0;
op.text.texture_index = G_MAXUINT;
for (i = 0, count = 0; i < glyphs->num_glyphs; i++)
{
PangoGlyphInfo *gi = &glyphs->glyphs[i];
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;
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;
}
case GSK_TEXTURE_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
@@ -600,11 +655,10 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
{
op->render.source = gsk_vulkan_renderer_ref_glyph_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
uploader,
gsk_text_node_get_font (op->render.node),
gsk_text_node_get_glyphs (op->render.node));
gsk_vulkan_render_add_cleanup_image (render, op->render.source);
op->text.source = gsk_vulkan_renderer_ref_glyph_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
uploader,
op->text.texture_index);
gsk_vulkan_render_add_cleanup_image (render, op->text.source);
}
break;
@@ -690,15 +744,15 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
break;
case GSK_VULKAN_OP_TEXT:
op->render.vertex_count = gsk_vulkan_text_pipeline_count_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->render.pipeline),
pango_glyph_string_num_glyphs (gsk_text_node_get_glyphs (op->render.node)));
n_bytes += op->render.vertex_count;
op->text.vertex_count = gsk_vulkan_text_pipeline_count_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
break;
case GSK_VULKAN_OP_COLOR_TEXT:
op->render.vertex_count = gsk_vulkan_color_text_pipeline_count_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->render.pipeline),
pango_glyph_string_num_glyphs (gsk_text_node_get_glyphs (op->render.node)));
n_bytes += op->render.vertex_count;
op->text.vertex_count = gsk_vulkan_color_text_pipeline_count_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->render.pipeline),
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
break;
case GSK_VULKAN_OP_COLOR:
@@ -777,32 +831,36 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_TEXT:
{
op->render.vertex_offset = offset + n_bytes;
gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->render.pipeline),
op->text.vertex_offset = offset + n_bytes;
gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
data + n_bytes + offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->render.node->bounds,
gsk_text_node_get_font (op->render.node),
gsk_text_node_get_glyphs (op->render.node),
gsk_text_node_get_color (op->render.node),
gsk_text_node_get_x (op->render.node),
gsk_text_node_get_y (op->render.node));
n_bytes += op->render.vertex_count;
&op->text.node->bounds,
gsk_text_node_get_font (op->text.node),
gsk_text_node_get_glyphs (op->text.node),
gsk_text_node_get_color (op->text.node),
gsk_text_node_get_x (op->text.node),
gsk_text_node_get_y (op->text.node),
op->text.start_glyph,
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
}
break;
case GSK_VULKAN_OP_COLOR_TEXT:
{
op->render.vertex_offset = offset + n_bytes;
gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->render.pipeline),
op->text.vertex_offset = offset + n_bytes;
gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->text.pipeline),
data + n_bytes + offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->render.node->bounds,
gsk_text_node_get_font (op->render.node),
gsk_text_node_get_glyphs (op->render.node),
gsk_text_node_get_x (op->render.node),
gsk_text_node_get_y (op->render.node));
n_bytes += op->render.vertex_count;
&op->text.node->bounds,
gsk_text_node_get_font (op->text.node),
gsk_text_node_get_glyphs (op->text.node),
gsk_text_node_get_x (op->text.node),
gsk_text_node_get_y (op->text.node),
op->text.start_glyph,
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
}
break;
@@ -948,8 +1006,6 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE:
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
case GSK_VULKAN_OP_TEXTURE:
case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_BLUR:
@@ -957,6 +1013,10 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source);
break;
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
op->text.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->text.source);
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_COLOR:
@@ -1027,9 +1087,9 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
break;
case GSK_VULKAN_OP_TEXT:
if (current_pipeline != op->render.pipeline)
if (current_pipeline != op->text.pipeline)
{
current_pipeline = op->render.pipeline;
current_pipeline = op->text.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
@@ -1039,7 +1099,7 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
(VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0;
}
@@ -1049,20 +1109,20 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_text_pipeline_draw (GSK_VULKAN_TEXT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, pango_glyph_string_num_glyphs (gsk_text_node_get_glyphs (op->render.node)));
command_buffer,
current_draw_index, op->text.num_glyphs);
break;
case GSK_VULKAN_OP_COLOR_TEXT:
if (current_pipeline != op->render.pipeline)
if (current_pipeline != op->text.pipeline)
{
current_pipeline = op->render.pipeline;
current_pipeline = op->text.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
@@ -1072,7 +1132,7 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
(VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0;
}
@@ -1082,14 +1142,14 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_color_text_pipeline_draw (GSK_VULKAN_COLOR_TEXT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, pango_glyph_string_num_glyphs (gsk_text_node_get_glyphs (op->render.node)));
current_draw_index, op->text.num_glyphs);
break;
case GSK_VULKAN_OP_OPACITY:

View File

@@ -107,15 +107,19 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
PangoGlyphString *glyphs,
const GdkRGBA *color,
float x,
float y)
float y,
guint start_glyph,
guint num_glyphs)
{
GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
int i, count;
int i;
int count = 0;
int x_position = 0;
float dx, dy, dw, dh;
count = 0;
for (i = 0; i < glyphs->num_glyphs; i++)
for (i = 0; i < start_glyph; i++)
x_position += glyphs->glyphs[i].geometry.width;
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
{
PangoGlyphInfo *gi = &glyphs->glyphs[i];
@@ -127,17 +131,20 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
{
GskVulkanTextInstance *instance = &instances[count];
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;
instance->tex_rect[3] = glyph->th;
instance->rect[0] = x + cx + glyph->draw_x;
instance->rect[1] = y + cy + glyph->draw_y;
instance->rect[2] = glyph->draw_width;
instance->rect[3] = glyph->draw_height;
gsk_vulkan_renderer_get_glyph_coords (renderer, font, gi->glyph,
&instance->tex_rect[0],
&instance->tex_rect[1],
&instance->tex_rect[2],
&instance->tex_rect[3],
&dx, &dy, &dw, &dh);
instance->rect[0] = x + cx + dx;
instance->rect[1] = y + cy + dy;
instance->rect[2] = dw;
instance->rect[3] = dh;
instance->color[0] = color->red;
instance->color[1] = color->green;
instance->color[2] = color->blue;

View File

@@ -28,7 +28,9 @@ void gsk_vulkan_text_pipeline_collect_vertex_data (GskVulka
PangoGlyphString *glyphs,
const GdkRGBA *color,
float x,
float y);
float y,
guint start_glyph,
guint num_glyphs);
gsize gsk_vulkan_text_pipeline_draw (GskVulkanTextPipeline *pipeline,
VkCommandBuffer command_buffer,
gsize offset,

View File

@@ -64,6 +64,7 @@ if have_vulkan
'gskvulkancolortextpipeline.c',
'gskvulkancommandpool.c',
'gskvulkaneffectpipeline.c',
'gskvulkanglyphcache.c',
'gskvulkanlineargradientpipeline.c',
'gskvulkanimage.c',
'gskvulkantextpipeline.c',