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 }, { "transforms", GSK_DEBUG_TRANSFORMS },
{ "surface", GSK_DEBUG_SURFACE }, { "surface", GSK_DEBUG_SURFACE },
{ "vulkan", GSK_DEBUG_VULKAN }, { "vulkan", GSK_DEBUG_VULKAN },
{ "fallback", GSK_DEBUG_FALLBACK } { "fallback", GSK_DEBUG_FALLBACK },
{ "glyphcache", GSK_DEBUG_GLYPH_CACHE }
}; };
#endif #endif

View File

@@ -14,7 +14,8 @@ typedef enum {
GSK_DEBUG_TRANSFORMS = 1 << 5, GSK_DEBUG_TRANSFORMS = 1 << 5,
GSK_DEBUG_SURFACE = 1 << 6, GSK_DEBUG_SURFACE = 1 << 6,
GSK_DEBUG_VULKAN = 1 << 7, GSK_DEBUG_VULKAN = 1 << 7,
GSK_DEBUG_FALLBACK = 1 << 8 GSK_DEBUG_FALLBACK = 1 << 8,
GSK_DEBUG_GLYPH_CACHE = 1 << 9
} GskDebugFlags; } GskDebugFlags;
#define GSK_DEBUG_ANY ((1 << 9) - 1) #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); desc = pango_font_describe (self->font);
s = pango_font_description_to_string (desc); 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++) for (i = 0; i < self->glyphs->num_glyphs; i++)
{ {
PangoGlyphInfo *glyph = &self->glyphs->glyphs[i]; PangoGlyphInfo *glyph = &self->glyphs->glyphs[i];
@@ -3912,7 +3914,6 @@ gsk_text_node_serialize (GskRenderNode *node)
glyph->attr.is_cluster_start); glyph->attr.is_cluster_start);
} }
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uiiii)"));
v = g_variant_new (GSK_TEXT_NODE_VARIANT_TYPE, v = g_variant_new (GSK_TEXT_NODE_VARIANT_TYPE,
s, s,
self->color.red, self->color.red,
@@ -3935,7 +3936,7 @@ gsk_text_node_deserialize (GVariant *variant,
{ {
PangoFont *font; PangoFont *font;
PangoGlyphString *glyphs; PangoGlyphString *glyphs;
GVariantIter iter; GVariantIter *iter;
GskRenderNode *result; GskRenderNode *result;
PangoGlyphInfo glyph; PangoGlyphInfo glyph;
PangoFontDescription *desc; PangoFontDescription *desc;
@@ -3950,7 +3951,7 @@ gsk_text_node_deserialize (GVariant *variant,
if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error)) if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error))
return NULL; return NULL;
g_variant_get (variant, "(&sddddiidda(uiiii))", g_variant_get (variant, "(&sdddddda(uiiii))",
&s, &color.red, &color.green, &color.blue, &color.alpha, &s, &color.red, &color.green, &color.blue, &color.alpha,
&x, &y, &iter); &x, &y, &iter);
@@ -3960,9 +3961,9 @@ gsk_text_node_deserialize (GVariant *variant,
font = pango_font_map_load_font (fontmap, context, desc); font = pango_font_map_load_font (fontmap, context, desc);
glyphs = pango_glyph_string_new (); 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; i = 0;
while (g_variant_iter_next (&iter, "(uiiii)", while (g_variant_iter_next (iter, "(uiiii)",
&glyph.glyph, &glyph.geometry.width, &glyph.glyph, &glyph.geometry.width,
&glyph.geometry.x_offset, &glyph.geometry.y_offset, &glyph.geometry.x_offset, &glyph.geometry.y_offset,
&cluster_start)) &cluster_start))
@@ -3971,6 +3972,7 @@ gsk_text_node_deserialize (GVariant *variant,
glyphs->glyphs[i] = glyph; glyphs->glyphs[i] = glyph;
i++; i++;
} }
g_variant_iter_free (iter);
result = gsk_text_node_new (font, glyphs, &color, x, y); /* FIXME: Avoid copying glyphs */ 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, PangoFont *font,
PangoGlyphString *glyphs, PangoGlyphString *glyphs,
float x, float x,
float y) float y,
guint start_glyph,
guint num_glyphs)
{ {
GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data; GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
int i, count; int i;
int count = 0;
int x_position = 0; int x_position = 0;
float dx, dy, dw, dh;
count = 0; for (i = 0; i < start_glyph; i++)
for (i = 0; i < glyphs->num_glyphs; i++) x_position += glyphs->glyphs[i].geometry.width;
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
{ {
PangoGlyphInfo *gi = &glyphs->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)) if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
{ {
GskVulkanColorTextInstance *instance = &instances[count]; GskVulkanColorTextInstance *instance = &instances[count];
GskVulkanCachedGlyph *glyph;
gsk_vulkan_renderer_get_glyph_coords (renderer, font, gi->glyph, glyph = gsk_vulkan_renderer_get_cached_glyph (renderer, font, gi->glyph);
&instance->tex_rect[0],
&instance->tex_rect[1], instance->tex_rect[0] = glyph->tx;
&instance->tex_rect[2], instance->tex_rect[1] = glyph->ty;
&instance->tex_rect[3], instance->tex_rect[2] = glyph->tw;
&dx, &dy, &dw, &dh); instance->tex_rect[3] = glyph->th;
instance->rect[0] = x + cx + dx;
instance->rect[1] = y + cy + dy; instance->rect[0] = x + cx + glyph->draw_x;
instance->rect[2] = dw; instance->rect[1] = y + cy + glyph->draw_y;
instance->rect[3] = dh; instance->rect[2] = glyph->draw_width;
instance->rect[3] = glyph->draw_height;
count++; count++;
} }

View File

@@ -27,7 +27,9 @@ void gsk_vulkan_color_text_pipeline_collect_vertex_data (Gs
PangoFont *font, PangoFont *font,
PangoGlyphString *glyphs, PangoGlyphString *glyphs,
float x, float x,
float y); float y,
guint start_glyph,
guint num_glyphs);
gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline, gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
VkCommandBuffer command_buffer, VkCommandBuffer command_buffer,
gsize offset, 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; GskVulkanCommandPool *command_pool;
GArray *before_barriers; GArray *before_buffer_barriers;
GArray *before_image_barriers;
VkCommandBuffer copy_buffer; VkCommandBuffer copy_buffer;
GArray *after_barriers; GArray *after_buffer_barriers;
GArray *after_image_barriers;
GSList *staging_image_free_list; GSList *staging_image_free_list;
GSList *staging_buffer_free_list; GSList *staging_buffer_free_list;
@@ -30,8 +32,11 @@ struct _GskVulkanImage
gsize width; gsize width;
gsize height; gsize height;
VkImageUsageFlags vk_usage;
VkImage vk_image; VkImage vk_image;
VkImageView vk_image_view; VkImageView vk_image_view;
VkImageLayout vk_image_layout;
VkAccessFlags vk_access;
GskVulkanMemory *memory; GskVulkanMemory *memory;
}; };
@@ -49,8 +54,11 @@ gsk_vulkan_uploader_new (GdkVulkanContext *context,
self->vulkan = g_object_ref (context); self->vulkan = g_object_ref (context);
self->command_pool = command_pool; self->command_pool = command_pool;
self->before_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier)); self->before_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
self->after_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier)); 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; return self;
} }
@@ -60,8 +68,10 @@ gsk_vulkan_uploader_free (GskVulkanUploader *self)
{ {
gsk_vulkan_uploader_reset (self); gsk_vulkan_uploader_reset (self);
g_array_unref (self->after_barriers); g_array_unref (self->after_buffer_barriers);
g_array_unref (self->before_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); g_object_unref (self->vulkan);
@@ -71,14 +81,51 @@ gsk_vulkan_uploader_free (GskVulkanUploader *self)
static void static void
gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self, gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self,
gboolean after, 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; GArray *array;
if (after) if (after)
array = self->after_barriers; array = self->after_buffer_barriers;
else else
array = self->before_barriers; array = self->before_buffer_barriers;
g_array_append_val (array, *barrier); g_array_append_val (array, *barrier);
} }
@@ -95,7 +142,7 @@ gsk_vulkan_uploader_get_copy_buffer (GskVulkanUploader *self)
void void
gsk_vulkan_uploader_upload (GskVulkanUploader *self) 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; VkCommandBuffer command_buffer;
@@ -105,14 +152,15 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0, 0,
0, NULL, 0, NULL,
0, NULL, self->before_buffer_barriers->len, (VkBufferMemoryBarrier *) self->before_buffer_barriers->data,
self->before_barriers->len, (VkImageMemoryBarrier *) self->before_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); 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 */ /* 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); VkCommandBuffer command_buffer = gsk_vulkan_uploader_get_copy_buffer (self);
vkCmdPipelineBarrier (command_buffer, vkCmdPipelineBarrier (command_buffer,
@@ -120,9 +168,10 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0, 0,
0, NULL, 0, NULL,
0, NULL, self->after_buffer_barriers->len, (VkBufferMemoryBarrier *) self->after_buffer_barriers->data,
self->after_barriers->len, (VkImageMemoryBarrier *) self->after_barriers->data); self->after_image_barriers->len, (VkImageMemoryBarrier *) self->after_image_barriers->data);
g_array_set_size (self->after_barriers, 0); 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) if (self->copy_buffer != VK_NULL_HANDLE)
@@ -135,9 +184,9 @@ gsk_vulkan_uploader_upload (GskVulkanUploader *self)
void void
gsk_vulkan_uploader_reset (GskVulkanUploader *self) 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; 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); g_slist_free_full (self->staging_image_free_list, g_object_unref);
self->staging_image_free_list = NULL; self->staging_image_free_list = NULL;
@@ -151,6 +200,8 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
gsize height, gsize height,
VkImageTiling tiling, VkImageTiling tiling,
VkImageUsageFlags usage, VkImageUsageFlags usage,
VkImageLayout layout,
VkAccessFlags access,
VkMemoryPropertyFlags memory) VkMemoryPropertyFlags memory)
{ {
VkMemoryRequirements requirements; VkMemoryRequirements requirements;
@@ -161,6 +212,9 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
self->vulkan = g_object_ref (context); self->vulkan = g_object_ref (context);
self->width = width; self->width = width;
self->height = height; 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), GSK_VK_CHECK (vkCreateImage, gdk_vulkan_context_get_device (context),
&(VkImageCreateInfo) { &(VkImageCreateInfo) {
@@ -175,7 +229,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
.tiling = tiling, .tiling = tiling,
.usage = usage, .usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED .initialLayout = self->vk_image_layout,
}, },
NULL, NULL,
&self->vk_image); &self->vk_image);
@@ -270,9 +324,10 @@ gsk_vulkan_image_new_from_data_via_staging_buffer (GskVulkanUploader *uploader,
{ {
GskVulkanImage *self; GskVulkanImage *self;
GskVulkanBuffer *staging; GskVulkanBuffer *staging;
gsize buffer_size = width * height * 4;
guchar *mem; 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); mem = gsk_vulkan_buffer_map (staging);
if (stride == width * 4) 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_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, self = gsk_vulkan_image_new (uploader->vulkan,
width, width,
height, height,
VK_IMAGE_TILING_OPTIMAL, 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); VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader, gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE, FALSE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_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
}
});
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader), vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
gsk_vulkan_buffer_get_buffer (staging), 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, gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE, TRUE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_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
}
});
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging); 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, staging = gsk_vulkan_image_new (uploader->vulkan,
width, width,
height, height,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
gsk_vulkan_image_upload_data (staging, data, width, height, stride); gsk_vulkan_image_upload_data (staging, data, width, height, stride);
self = gsk_vulkan_image_new (uploader->vulkan, self = gsk_vulkan_image_new (uploader->vulkan,
width, width,
height, height,
VK_IMAGE_TILING_OPTIMAL, 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); VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_uploader_add_image_barrier (uploader, gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE, FALSE,
&(VkImageMemoryBarrier) { staging,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_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
}
});
gsk_vulkan_uploader_add_image_barrier (uploader, gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE, FALSE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_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
}
});
vkCmdCopyImage (gsk_vulkan_uploader_get_copy_buffer (uploader), vkCmdCopyImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
staging->vk_image, staging->vk_image,
@@ -451,7 +473,7 @@ gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
.layerCount = 1 .layerCount = 1
}, },
.dstOffset = { 0, 0, 0 }, .dstOffset = { 0, 0, 0 },
.extent = { .extent = {
.width = width, .width = width,
.height = height, .height = height,
.depth = 1 .depth = 1
@@ -460,23 +482,9 @@ gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
gsk_vulkan_uploader_add_image_barrier (uploader, gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE, TRUE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_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
}
});
uploader->staging_image_free_list = g_slist_prepend (uploader->staging_image_free_list, staging); 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, self = gsk_vulkan_image_new (uploader->vulkan,
width, width,
height, height,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_ACCESS_HOST_WRITE_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
gsk_vulkan_image_upload_data (self, data, width, height, stride); gsk_vulkan_image_upload_data (self, data, width, height, stride);
gsk_vulkan_uploader_add_image_barrier (uploader, gsk_vulkan_uploader_add_image_barrier (uploader,
TRUE, TRUE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_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
}
});
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM); 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, self = gsk_vulkan_image_new (context,
width, width,
height, height,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 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); VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM); 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, gsk_vulkan_uploader_add_image_barrier (uploader,
FALSE, FALSE,
&(VkImageMemoryBarrier) { self,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_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
}
});
buffer = gsk_vulkan_buffer_new_download (self->vulkan, self->width * self->height * 4); 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; 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 static void
gsk_vulkan_image_finalize (GObject *object) gsk_vulkan_image_finalize (GObject *object)
{ {
@@ -711,4 +786,3 @@ gsk_vulkan_image_get_image_view (GskVulkanImage *self)
{ {
return self->vk_image_view; return self->vk_image_view;
} }

View File

@@ -31,9 +31,20 @@ GskVulkanImage * gsk_vulkan_image_new_from_data (GskVulk
gsize width, gsize width,
gsize height, gsize height,
gsize stride); 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, GskVulkanImage * gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
gsize width, gsize width,
gsize height); gsize height);
GskVulkanImage * gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
gsize width,
gsize height);
GskTexture * gsk_vulkan_image_download (GskVulkanImage *self, GskTexture * gsk_vulkan_image_download (GskVulkanImage *self,
GskVulkanUploader *uploader); GskVulkanUploader *uploader);

View File

@@ -11,6 +11,7 @@
#include "gskvulkanimageprivate.h" #include "gskvulkanimageprivate.h"
#include "gskvulkanpipelineprivate.h" #include "gskvulkanpipelineprivate.h"
#include "gskvulkanrenderprivate.h" #include "gskvulkanrenderprivate.h"
#include "gskvulkanglyphcacheprivate.h"
#include <graphene.h> #include <graphene.h>
@@ -29,23 +30,6 @@ typedef struct {
} ProfileTimers; } ProfileTimers;
#endif #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 struct _GskVulkanRenderer
{ {
GskRenderer parent_instance; GskRenderer parent_instance;
@@ -61,7 +45,7 @@ struct _GskVulkanRenderer
GSList *textures; GSList *textures;
GlyphCache *glyph_cache; GskVulkanGlyphCache *glyph_cache;
#ifdef G_ENABLE_DEBUG #ifdef G_ENABLE_DEBUG
ProfileTimers profile_timers; ProfileTimers profile_timers;
@@ -140,7 +124,8 @@ gsk_vulkan_renderer_realize (GskRenderer *renderer,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
.unnormalizedCoordinates = VK_FALSE .unnormalizedCoordinates = VK_FALSE,
.maxAnisotropy = 1.0,
}, },
NULL, NULL,
&self->sampler); &self->sampler);
@@ -153,7 +138,7 @@ gsk_vulkan_renderer_realize (GskRenderer *renderer,
self->render = gsk_vulkan_render_new (renderer, self->vulkan); 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; return TRUE;
} }
@@ -165,8 +150,7 @@ gsk_vulkan_renderer_unrealize (GskRenderer *renderer)
VkDevice device; VkDevice device;
GSList *l; GSList *l;
free_glyph_cache (self->glyph_cache); g_clear_object (&self->glyph_cache);
self->glyph_cache = NULL;
for (l = self->textures; l; l = l->next) 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); GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
GdkDrawingContext *result; GdkDrawingContext *result;
gsk_vulkan_glyph_cache_begin_frame (self->glyph_cache);
#if 0
GSK_NOTE(RENDERER, dump_glyph_cache_stats (self->glyph_cache)); GSK_NOTE(RENDERER, dump_glyph_cache_stats (self->glyph_cache));
#endif
result = gdk_window_begin_draw_frame (gsk_renderer_get_window (renderer), result = gdk_window_begin_draw_frame (gsk_renderer_get_window (renderer),
GDK_DRAW_CONTEXT (self->vulkan), GDK_DRAW_CONTEXT (self->vulkan),
@@ -370,266 +358,26 @@ gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
return image; 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 * GskVulkanImage *
gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self, gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
GskVulkanUploader *uploader, GskVulkanUploader *uploader,
PangoFont *font, guint index)
PangoGlyphString *glyphs)
{ {
if (self->glyph_cache->image == NULL) return g_object_ref (gsk_vulkan_glyph_cache_get_glyph_image (self->glyph_cache, uploader, index));
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);
} }
typedef struct _GlyphCacheKey GlyphCacheKey; GskVulkanCachedGlyph *
typedef struct _GlyphCacheValue GlyphCacheValue; gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
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,
PangoFont *font, PangoFont *font,
PangoGlyph glyph, PangoGlyph glyph)
float *tx,
float *ty,
float *tw,
float *th,
float *dx,
float *dy,
float *dw,
float *dh)
{ {
GlyphCacheValue *gv; return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, FALSE, font, glyph);
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");
} }

View File

@@ -25,26 +25,35 @@ GskVulkanImage * gsk_vulkan_renderer_ref_texture_image (GskVulk
GskTexture *texture, GskTexture *texture,
GskVulkanUploader *uploader); GskVulkanUploader *uploader);
GskVulkanImage * gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self, typedef struct
GskVulkanUploader *uploader, {
PangoFont *font, guint texture_index;
PangoGlyphString *glyphs);
void gsk_vulkan_renderer_get_glyph_coords (GskVulkanRenderer *self, float tx;
PangoFont *font, float ty;
PangoGlyph glyph, float tw;
float *tx, float th;
float *ty,
float *tw, int draw_x;
float *th, int draw_y;
float *dx, int draw_width;
float *dy, int draw_height;
float *dw,
float *dh); 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 G_END_DECLS

View File

@@ -25,6 +25,7 @@
typedef union _GskVulkanOp GskVulkanOp; typedef union _GskVulkanOp GskVulkanOp;
typedef struct _GskVulkanOpRender GskVulkanOpRender; typedef struct _GskVulkanOpRender GskVulkanOpRender;
typedef struct _GskVulkanOpText GskVulkanOpText;
typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants; typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants;
typedef enum { typedef enum {
@@ -60,6 +61,21 @@ struct _GskVulkanOpRender
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */ 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 struct _GskVulkanOpPushConstants
{ {
GskVulkanOpType type; GskVulkanOpType type;
@@ -72,6 +88,7 @@ union _GskVulkanOp
GskVulkanOpType type; GskVulkanOpType type;
GskVulkanOpRender render; GskVulkanOpRender render;
GskVulkanOpPushConstants constants; GskVulkanOpPushConstants constants;
GskVulkanOpText text;
}; };
struct _GskVulkanRenderPass struct _GskVulkanRenderPass
@@ -197,37 +214,75 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
return; return;
case GSK_TEXT_NODE: case GSK_TEXT_NODE:
gsk_vulkan_renderer_cache_glyphs (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)), {
gsk_text_node_get_font (node), PangoFont *font = gsk_text_node_get_font (node);
gsk_text_node_get_glyphs (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 (font_has_color_glyphs (font))
{ {
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT; pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP; pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED; pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED;
else else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type); FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_COLOR_TEXT; op.type = GSK_VULKAN_OP_COLOR_TEXT;
} }
else else
{ {
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXT; pipeline_type = GSK_VULKAN_PIPELINE_TEXT;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP; pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED; pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED;
else else
FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type); FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_TEXT; op.type = GSK_VULKAN_OP_TEXT;
} }
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return; 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: case GSK_TEXTURE_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) 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_TEXT:
case GSK_VULKAN_OP_COLOR_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)), op->text.source = gsk_vulkan_renderer_ref_glyph_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
uploader, uploader,
gsk_text_node_get_font (op->render.node), op->text.texture_index);
gsk_text_node_get_glyphs (op->render.node)); gsk_vulkan_render_add_cleanup_image (render, op->text.source);
gsk_vulkan_render_add_cleanup_image (render, op->render.source);
} }
break; break;
@@ -690,15 +744,15 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
break; break;
case GSK_VULKAN_OP_TEXT: case GSK_VULKAN_OP_TEXT:
op->render.vertex_count = gsk_vulkan_text_pipeline_count_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->render.pipeline), op->text.vertex_count = gsk_vulkan_text_pipeline_count_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
pango_glyph_string_num_glyphs (gsk_text_node_get_glyphs (op->render.node))); op->text.num_glyphs);
n_bytes += op->render.vertex_count; n_bytes += op->text.vertex_count;
break; break;
case GSK_VULKAN_OP_COLOR_TEXT: 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), op->text.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))); op->text.num_glyphs);
n_bytes += op->render.vertex_count; n_bytes += op->text.vertex_count;
break; break;
case GSK_VULKAN_OP_COLOR: case GSK_VULKAN_OP_COLOR:
@@ -777,32 +831,36 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_TEXT: case GSK_VULKAN_OP_TEXT:
{ {
op->render.vertex_offset = offset + n_bytes; op->text.vertex_offset = offset + n_bytes;
gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->render.pipeline), gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
data + n_bytes + offset, data + n_bytes + offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)), GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->render.node->bounds, &op->text.node->bounds,
gsk_text_node_get_font (op->render.node), gsk_text_node_get_font (op->text.node),
gsk_text_node_get_glyphs (op->render.node), gsk_text_node_get_glyphs (op->text.node),
gsk_text_node_get_color (op->render.node), gsk_text_node_get_color (op->text.node),
gsk_text_node_get_x (op->render.node), gsk_text_node_get_x (op->text.node),
gsk_text_node_get_y (op->render.node)); gsk_text_node_get_y (op->text.node),
n_bytes += op->render.vertex_count; op->text.start_glyph,
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
} }
break; break;
case GSK_VULKAN_OP_COLOR_TEXT: case GSK_VULKAN_OP_COLOR_TEXT:
{ {
op->render.vertex_offset = offset + n_bytes; op->text.vertex_offset = offset + n_bytes;
gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->render.pipeline), gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->text.pipeline),
data + n_bytes + offset, data + n_bytes + offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)), GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->render.node->bounds, &op->text.node->bounds,
gsk_text_node_get_font (op->render.node), gsk_text_node_get_font (op->text.node),
gsk_text_node_get_glyphs (op->render.node), gsk_text_node_get_glyphs (op->text.node),
gsk_text_node_get_x (op->render.node), gsk_text_node_get_x (op->text.node),
gsk_text_node_get_y (op->render.node)); gsk_text_node_get_y (op->text.node),
n_bytes += op->render.vertex_count; op->text.start_glyph,
op->text.num_glyphs);
n_bytes += op->text.vertex_count;
} }
break; 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_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE: 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_TEXTURE:
case GSK_VULKAN_OP_OPACITY: case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_BLUR: 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); op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source);
break; 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: default:
g_assert_not_reached (); g_assert_not_reached ();
case GSK_VULKAN_OP_COLOR: case GSK_VULKAN_OP_COLOR:
@@ -1027,9 +1087,9 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
break; break;
case GSK_VULKAN_OP_TEXT: 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, vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline)); gsk_vulkan_pipeline_get_pipeline (current_pipeline));
@@ -1039,7 +1099,7 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
(VkBuffer[1]) { (VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer) gsk_vulkan_buffer_get_buffer (vertex_buffer)
}, },
(VkDeviceSize[1]) { op->render.vertex_offset }); (VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0; current_draw_index = 0;
} }
@@ -1049,20 +1109,20 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
0, 0,
1, 1,
(VkDescriptorSet[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, 0,
NULL); NULL);
current_draw_index += gsk_vulkan_text_pipeline_draw (GSK_VULKAN_TEXT_PIPELINE (current_pipeline), current_draw_index += gsk_vulkan_text_pipeline_draw (GSK_VULKAN_TEXT_PIPELINE (current_pipeline),
command_buffer, 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; break;
case GSK_VULKAN_OP_COLOR_TEXT: 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, vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline)); gsk_vulkan_pipeline_get_pipeline (current_pipeline));
@@ -1072,7 +1132,7 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
(VkBuffer[1]) { (VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer) gsk_vulkan_buffer_get_buffer (vertex_buffer)
}, },
(VkDeviceSize[1]) { op->render.vertex_offset }); (VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0; current_draw_index = 0;
} }
@@ -1082,14 +1142,14 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
0, 0,
1, 1,
(VkDescriptorSet[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, 0,
NULL); NULL);
current_draw_index += gsk_vulkan_color_text_pipeline_draw (GSK_VULKAN_COLOR_TEXT_PIPELINE (current_pipeline), current_draw_index += gsk_vulkan_color_text_pipeline_draw (GSK_VULKAN_COLOR_TEXT_PIPELINE (current_pipeline),
command_buffer, 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; break;
case GSK_VULKAN_OP_OPACITY: case GSK_VULKAN_OP_OPACITY:

View File

@@ -107,15 +107,19 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
PangoGlyphString *glyphs, PangoGlyphString *glyphs,
const GdkRGBA *color, const GdkRGBA *color,
float x, float x,
float y) float y,
guint start_glyph,
guint num_glyphs)
{ {
GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data; GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
int i, count; int i;
int count = 0;
int x_position = 0; int x_position = 0;
float dx, dy, dw, dh;
count = 0; for (i = 0; i < start_glyph; i++)
for (i = 0; i < glyphs->num_glyphs; i++) x_position += glyphs->glyphs[i].geometry.width;
for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
{ {
PangoGlyphInfo *gi = &glyphs->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)) if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
{ {
GskVulkanTextInstance *instance = &instances[count]; 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[0] = color->red;
instance->color[1] = color->green; instance->color[1] = color->green;
instance->color[2] = color->blue; instance->color[2] = color->blue;

View File

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

View File

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