summaryrefslogtreecommitdiff
path: root/gsk
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2017-09-01 16:58:42 -0400
committerMatthias Clasen <mclasen@redhat.com>2017-09-10 14:36:26 -0400
commitb0e8d8483d2b46dfc4d678e4a217d540837a2478 (patch)
tree6d85e2f8ac33c4ece118dec754ac1a8ad3c80404 /gsk
parentedc73e78b8e312f5e52f9d464baebe7a5d0fe581 (diff)
downloadgtk+-b0e8d8483d2b46dfc4d678e4a217d540837a2478.tar.gz
More work on text nodes
This commit takes several steps towards rendering text like we want to. The creation of the cairo surface and texture is moved to the backend (in GskVulkanRenderer). We add a mask shader that is used in the next text pipeline to use the texture as a mask, like cairo_mask_surface does. There is a separate color text pipeline that uses the already existing blend shaders to use the texture as a source, like cairo_paint does. The text node api is simplified to have just a single offset, which determines the left end of the text baseline, like all our other text drawing APIs.
Diffstat (limited to 'gsk')
-rw-r--r--gsk/gskprivate.c20
-rw-r--r--gsk/gskprivate.h3
-rw-r--r--gsk/gskrendernode.h6
-rw-r--r--gsk/gskrendernodeimpl.c154
-rw-r--r--gsk/gskrendernodeprivate.h6
-rw-r--r--gsk/gskvulkancolortextpipeline.c161
-rw-r--r--gsk/gskvulkancolortextpipelineprivate.h38
-rw-r--r--gsk/gskvulkanpipeline.c21
-rw-r--r--gsk/gskvulkanpipelineprivate.h6
-rw-r--r--gsk/gskvulkanrender.c10
-rw-r--r--gsk/gskvulkanrenderer.c141
-rw-r--r--gsk/gskvulkanrendererprivate.h14
-rw-r--r--gsk/gskvulkanrenderpass.c180
-rw-r--r--gsk/gskvulkanrenderpassprivate.h1
-rw-r--r--gsk/gskvulkanrenderprivate.h6
-rw-r--r--gsk/gskvulkantextpipeline.c173
-rw-r--r--gsk/gskvulkantextpipelineprivate.h39
-rw-r--r--gsk/meson.build2
-rw-r--r--gsk/resources/vulkan/mask-clip-rounded.frag.spvbin0 -> 8508 bytes
-rw-r--r--gsk/resources/vulkan/mask-clip-rounded.vert.spvbin0 -> 5016 bytes
-rw-r--r--gsk/resources/vulkan/mask-clip.frag.spvbin0 -> 1600 bytes
-rw-r--r--gsk/resources/vulkan/mask-clip.vert.spvbin0 -> 5016 bytes
-rw-r--r--gsk/resources/vulkan/mask.frag16
-rw-r--r--gsk/resources/vulkan/mask.frag.spvbin0 -> 1600 bytes
-rw-r--r--gsk/resources/vulkan/mask.vert38
-rw-r--r--gsk/resources/vulkan/mask.vert.spvbin0 -> 3284 bytes
-rw-r--r--gsk/resources/vulkan/meson.build2
27 files changed, 953 insertions, 84 deletions
diff --git a/gsk/gskprivate.c b/gsk/gskprivate.c
index 8404b9dbc6..d9e1c3e30f 100644
--- a/gsk/gskprivate.c
+++ b/gsk/gskprivate.c
@@ -15,3 +15,23 @@ gsk_ensure_resources (void)
g_once (&register_resources_once, register_resources, NULL);
}
+
+int
+pango_glyph_string_num_glyphs (PangoGlyphString *glyphs)
+{
+ int i, count;
+
+ count = 0;
+ 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))
+ count++;
+ }
+ }
+
+ return count;
+}
+
diff --git a/gsk/gskprivate.h b/gsk/gskprivate.h
index 84539c1557..8584be6960 100644
--- a/gsk/gskprivate.h
+++ b/gsk/gskprivate.h
@@ -2,11 +2,14 @@
#define __GSK_PRIVATE_H__
#include <glib.h>
+#include <pango/pango.h>
G_BEGIN_DECLS
void gsk_ensure_resources (void);
+int pango_glyph_string_num_glyphs (PangoGlyphString *glyphs);
+
G_END_DECLS
#endif /* __GSK_PRIVATE_H__ */
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 46f6094ec3..3816e1c58f 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -178,10 +178,8 @@ GDK_AVAILABLE_IN_3_92
GskRenderNode * gsk_text_node_new (PangoFont *font,
PangoGlyphString *glyphs,
const GdkRGBA *color,
- int x_offset,
- int y_offset,
- double base_x,
- double base_y);
+ double x,
+ double y);
GDK_AVAILABLE_IN_3_92
GskRenderNode * gsk_blur_node_new (GskRenderNode *child,
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 6ddbb2b431..a71bea7515 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -26,8 +26,6 @@
#include "gskroundedrectprivate.h"
#include "gsktextureprivate.h"
-#include <cairo-ft.h>
-
static gboolean
check_variant_type (GVariant *variant,
const char *type_string,
@@ -3812,13 +3810,11 @@ struct _GskTextNode
GskRenderNode render_node;
PangoFont *font;
- gboolean has_color;
PangoGlyphString *glyphs;
+
GdkRGBA color;
- int x_offset;
- int y_offset;
- double base_x;
- double base_y;
+ double x;
+ double y;
};
static void
@@ -3830,20 +3826,6 @@ gsk_text_node_finalize (GskRenderNode *node)
pango_glyph_string_free (self->glyphs);
}
-static gboolean
-_pango_cairo_font_install (PangoFont *font,
- cairo_t *cr)
-{
- cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
-
- if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
- return FALSE;
-
- cairo_set_scaled_font (cr, scaled_font);
-
- return TRUE;
-}
-
#ifndef STACK_BUFFER_SIZE
#define STACK_BUFFER_SIZE (512 * sizeof (int))
#endif
@@ -3857,16 +3839,19 @@ gsk_text_node_draw (GskRenderNode *node,
GskTextNode *self = (GskTextNode *) node;
int i, count;
int x_position = 0;
+ cairo_scaled_font_t *scaled_font;
cairo_glyph_t *cairo_glyphs;
cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
- cairo_save (cr);
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)self->font);
+ if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+ return;
- cairo_translate (cr, self->x_offset, self->y_offset);
+ cairo_save (cr);
+ cairo_translate (cr, self->x, self->y);
+ cairo_set_scaled_font (cr, scaled_font);
gdk_cairo_set_source_rgba (cr, &self->color);
- if (!_pango_cairo_font_install (self->font, cr))
- goto done;
if (self->glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
cairo_glyphs = g_new (cairo_glyph_t, self->glyphs->num_glyphs);
@@ -3880,8 +3865,8 @@ gsk_text_node_draw (GskRenderNode *node,
if (gi->glyph != PANGO_GLYPH_EMPTY)
{
- double cx = self->base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
- double cy = gi->geometry.y_offset == 0 ? self->base_y : self->base_y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
+ double cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
{
@@ -3899,11 +3884,10 @@ gsk_text_node_draw (GskRenderNode *node,
if (cairo_glyphs != stack_glyphs)
g_free (cairo_glyphs);
-done:
cairo_restore (cr);
}
-#define GSK_TEXT_NODE_VARIANT_TYPE "(sddddiidda(uiiii))"
+#define GSK_TEXT_NODE_VARIANT_TYPE "(sdddddda(uiiii))"
static GVariant *
gsk_text_node_serialize (GskRenderNode *node)
@@ -3935,10 +3919,8 @@ gsk_text_node_serialize (GskRenderNode *node)
self->color.green,
self->color.blue,
self->color.alpha,
- self->x_offset,
- self->y_offset,
- self->base_x,
- self->base_y,
+ self->x,
+ self->y,
&builder);
g_free (s);
@@ -3962,18 +3944,15 @@ gsk_text_node_deserialize (GVariant *variant,
int cluster_start;
char *s;
GdkRGBA color;
- int x_offset, y_offset;
- double base_x, base_y;
+ double x, y;
int i;
if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error))
return NULL;
g_variant_get (variant, "(&sddddiidda(uiiii))",
- &color.red, &color.green, &color.blue, &color.alpha,
- &x_offset, &y_offset,
- &base_x, &base_y,
- &s, &iter);
+ &s, &color.red, &color.green, &color.blue, &color.alpha,
+ &x, &y, &iter);
desc = pango_font_description_from_string (s);
fontmap = pango_cairo_font_map_get_default ();
@@ -3983,16 +3962,17 @@ gsk_text_node_deserialize (GVariant *variant,
glyphs = pango_glyph_string_new ();
pango_glyph_string_set_size (glyphs, g_variant_iter_n_children (&iter));
i = 0;
- while (g_variant_iter_next (&iter, "(uiiii)", &glyph.glyph, &glyph.geometry.width, &glyph.geometry.x_offset, &glyph.geometry.y_offset, &cluster_start))
+ while (g_variant_iter_next (&iter, "(uiiii)",
+ &glyph.glyph, &glyph.geometry.width,
+ &glyph.geometry.x_offset, &glyph.geometry.y_offset,
+ &cluster_start))
{
glyph.attr.is_cluster_start = cluster_start;
glyphs->glyphs[i] = glyph;
i++;
}
- result = gsk_text_node_new (font, glyphs, &color, /* FIXME: Avoid copying glyphs */
- x_offset, y_offset,
- base_x, base_y);
+ result = gsk_text_node_new (font, glyphs, &color, x, y); /* FIXME: Avoid copying glyphs */
pango_glyph_string_free (glyphs);
pango_font_description_free (desc);
@@ -4012,31 +3992,12 @@ static const GskRenderNodeClass GSK_TEXT_NODE_CLASS = {
gsk_text_node_deserialize
};
-static gboolean
-font_has_color_glyphs (PangoFont *font)
-{
- cairo_scaled_font_t *scaled_font;
- gboolean has_color = FALSE;
-
- scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
- if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
- {
- FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
- has_color = (FT_HAS_COLOR (ft_face) != 0);
- cairo_ft_scaled_font_unlock_face (scaled_font);
- }
-
- return has_color;
-}
-
GskRenderNode *
gsk_text_node_new (PangoFont *font,
PangoGlyphString *glyphs,
const GdkRGBA *color,
- int x_offset,
- int y_offset,
- double base_x,
- double base_y)
+ double x,
+ double y)
{
GskTextNode *self;
PangoRectangle ink_rect;
@@ -4053,23 +4014,68 @@ gsk_text_node_new (PangoFont *font,
self->font = g_object_ref (font);
self->glyphs = pango_glyph_string_copy (glyphs);
self->color = *color;
- self->x_offset = x_offset;
- self->y_offset = y_offset;
- self->base_x = base_x;
- self->base_y = base_y;
-
- self->has_color = font_has_color_glyphs (font);
-
+ self->x = x;
+ self->y = y;
graphene_rect_init (&self->render_node.bounds,
- x_offset + base_x + ink_rect.x,
- y_offset + base_y + ink_rect.y,
- ink_rect.width,
+ x,
+ y + ink_rect.y,
+ ink_rect.x + ink_rect.width,
ink_rect.height);
return &self->render_node;
}
+const GdkRGBA *
+gsk_text_node_get_color (GskRenderNode *node)
+{
+ GskTextNode *self = (GskTextNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), NULL);
+
+ return &self->color;
+}
+
+PangoFont *
+gsk_text_node_get_font (GskRenderNode *node)
+{
+ GskTextNode *self = (GskTextNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), NULL);
+
+ return self->font;
+}
+
+PangoGlyphString *
+gsk_text_node_get_glyphs (GskRenderNode *node)
+{
+ GskTextNode *self = (GskTextNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), NULL);
+
+ return self->glyphs;
+}
+
+float
+gsk_text_node_get_x (GskRenderNode *node)
+{
+ GskTextNode *self = (GskTextNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), 0.0);
+
+ return (float)self->x;
+}
+
+float
+gsk_text_node_get_y (GskRenderNode *node)
+{
+ GskTextNode *self = (GskTextNode *) node;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), 0.0);
+
+ return (float)self->y;
+}
+
/*** GSK_BLUR_NODE ***/
typedef struct _GskBlurNode GskBlurNode;
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index be29f8725c..4934ea3817 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -81,6 +81,12 @@ cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node);
GskTexture *gsk_texture_node_get_texture (GskRenderNode *node);
+PangoFont *gsk_text_node_get_font (GskRenderNode *node);
+PangoGlyphString *gsk_text_node_get_glyphs (GskRenderNode *node);
+const GdkRGBA *gsk_text_node_get_color (GskRenderNode *node);
+float gsk_text_node_get_x (GskRenderNode *node);
+float gsk_text_node_get_y (GskRenderNode *node);
+
const GdkRGBA *gsk_color_node_peek_color (GskRenderNode *node);
const graphene_rect_t * gsk_clip_node_peek_clip (GskRenderNode *node);
diff --git a/gsk/gskvulkancolortextpipeline.c b/gsk/gskvulkancolortextpipeline.c
new file mode 100644
index 0000000000..5e3bafb3d1
--- /dev/null
+++ b/gsk/gskvulkancolortextpipeline.c
@@ -0,0 +1,161 @@
+#include "config.h"
+
+#include "gskvulkancolortextpipelineprivate.h"
+
+struct _GskVulkanColorTextPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanColorTextInstance GskVulkanColorTextInstance;
+
+struct _GskVulkanColorTextInstance
+{
+ float rect[4];
+ float tex_rect[4];
+};
+
+G_DEFINE_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_color_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanColorTextInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, tex_rect),
+ },
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_color_text_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanColorTextPipeline *self = GSK_VULKAN_COLOR_TEXT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_color_text_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_color_text_pipeline_class_init (GskVulkanColorTextPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_color_text_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_color_text_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_color_text_pipeline_init (GskVulkanColorTextPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_color_text_pipeline_new (GskVulkanPipelineLayout *layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new_full (GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE, layout, shader_name, render_pass,
+ VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+gsize
+gsk_vulkan_color_text_pipeline_count_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ int num_instances)
+{
+ return sizeof (GskVulkanColorTextInstance) * num_instances;
+}
+
+void
+gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ float x,
+ float y)
+{
+ GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
+ int i, count;
+ int x_position = 0;
+ float ink_rect_y;
+ float ink_rect_height;
+ GskGlyphCoords *coords;
+
+ /* XXX */
+ ink_rect_y = rect->origin.y - y;
+ ink_rect_height = rect->size.height;
+
+ coords = g_new (GskGlyphCoords, glyphs->num_glyphs);
+ gsk_vulkan_renderer_get_glyph_coords (renderer, font, glyphs, coords);
+
+ count = 0;
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
+
+ if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ GskVulkanColorTextInstance *instance = &instances[count];
+
+ instance->rect[0] = x + cx;
+ instance->rect[1] = y + ink_rect_y + cy;
+ instance->rect[2] = (float)gi->geometry.width / PANGO_SCALE;
+ instance->rect[3] = ink_rect_height;
+ instance->tex_rect[0] = coords[i].x;
+ instance->tex_rect[1] = coords[i].y;
+ instance->tex_rect[2] = coords[i].width;
+ instance->tex_rect[3] = coords[i].height;
+
+ count++;
+ }
+ }
+ x_position += gi->geometry.width;
+ }
+
+ g_free (coords);
+}
+
+gsize
+gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/gskvulkancolortextpipelineprivate.h b/gsk/gskvulkancolortextpipelineprivate.h
new file mode 100644
index 0000000000..b341abde34
--- /dev/null
+++ b/gsk/gskvulkancolortextpipelineprivate.h
@@ -0,0 +1,38 @@
+#ifndef __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrendererprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanColorTextPipelineLayout GskVulkanColorTextPipelineLayout;
+
+#define GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE (gsk_vulkan_color_text_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK, VULKAN_COLOR_TEXT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_color_text_pipeline_new (GskVulkanPipelineLayout *layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_color_text_pipeline_count_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ int num_instances);
+void gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ float x,
+ float y);
+gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/gskvulkanpipeline.c b/gsk/gskvulkanpipeline.c
index 7fff1d2760..71ea27e814 100644
--- a/gsk/gskvulkanpipeline.c
+++ b/gsk/gskvulkanpipeline.c
@@ -68,6 +68,19 @@ gsk_vulkan_pipeline_new (GType pipeline_type,
const char *shader_name,
VkRenderPass render_pass)
{
+ return gsk_vulkan_pipeline_new_full (pipeline_type, layout, shader_name, render_pass,
+ VK_BLEND_FACTOR_ONE,
+ VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+GskVulkanPipeline *
+gsk_vulkan_pipeline_new_full (GType pipeline_type,
+ GskVulkanPipelineLayout *layout,
+ const char *shader_name,
+ VkRenderPass render_pass,
+ VkBlendFactor srcBlendFactor,
+ VkBlendFactor dstBlendFactor)
+{
GskVulkanPipelinePrivate *priv;
GskVulkanPipeline *self;
@@ -134,11 +147,11 @@ gsk_vulkan_pipeline_new (GType pipeline_type,
{
.blendEnable = VK_TRUE,
.colorBlendOp = VK_BLEND_OP_ADD,
- .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
- .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .srcColorBlendFactor = srcBlendFactor,
+ .dstColorBlendFactor = dstBlendFactor,
.alphaBlendOp = VK_BLEND_OP_ADD,
- .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
- .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .srcAlphaBlendFactor = srcBlendFactor,
+ .dstAlphaBlendFactor = dstBlendFactor,
.colorWriteMask = VK_COLOR_COMPONENT_A_BIT
| VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
diff --git a/gsk/gskvulkanpipelineprivate.h b/gsk/gskvulkanpipelineprivate.h
index 91ffee8f38..504306a714 100644
--- a/gsk/gskvulkanpipelineprivate.h
+++ b/gsk/gskvulkanpipelineprivate.h
@@ -48,6 +48,12 @@ GskVulkanPipeline * gsk_vulkan_pipeline_new (GType
GskVulkanPipelineLayout *layout,
const char *shader_name,
VkRenderPass render_pass);
+GskVulkanPipeline * gsk_vulkan_pipeline_new_full (GType pipeline_type,
+ GskVulkanPipelineLayout *layout,
+ const char *shader_name,
+ VkRenderPass render_pass,
+ VkBlendFactor srcBlendFactor,
+ VkBlendFactor dstBlendFactor);
VkPipeline gsk_vulkan_pipeline_get_pipeline (GskVulkanPipeline *self);
diff --git a/gsk/gskvulkanrender.c b/gsk/gskvulkanrender.c
index 4da8e48566..63988295b1 100644
--- a/gsk/gskvulkanrender.c
+++ b/gsk/gskvulkanrender.c
@@ -15,8 +15,10 @@
#include "gskvulkanborderpipelineprivate.h"
#include "gskvulkanboxshadowpipelineprivate.h"
#include "gskvulkancolorpipelineprivate.h"
+#include "gskvulkancolortextpipelineprivate.h"
#include "gskvulkaneffectpipelineprivate.h"
#include "gskvulkanlineargradientpipelineprivate.h"
+#include "gskvulkantextpipelineprivate.h"
#define ORTHO_NEAR_PLANE -10000
#define ORTHO_FAR_PLANE 10000
@@ -303,7 +305,7 @@ gsk_vulkan_render_collect_vertex_data (GskVulkanRender *self)
for (l = self->render_passes; l; l = l->next)
{
- offset += gsk_vulkan_render_pass_collect_vertex_data (l->data, data, offset, n_bytes - offset);
+ offset += gsk_vulkan_render_pass_collect_vertex_data (l->data, self, data, offset, n_bytes - offset);
g_assert (offset <= n_bytes);
}
@@ -344,6 +346,12 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
{ "blur", gsk_vulkan_blur_pipeline_new },
{ "blur-clip", gsk_vulkan_blur_pipeline_new },
{ "blur-clip-rounded", gsk_vulkan_blur_pipeline_new },
+ { "mask", gsk_vulkan_text_pipeline_new },
+ { "mask-clip", gsk_vulkan_text_pipeline_new },
+ { "mask-clip-rounded", gsk_vulkan_text_pipeline_new },
+ { "blend", gsk_vulkan_color_text_pipeline_new },
+ { "blend-clip", gsk_vulkan_color_text_pipeline_new },
+ { "blend-clip-rounded", gsk_vulkan_color_text_pipeline_new },
};
g_return_val_if_fail (type < GSK_VULKAN_N_PIPELINES, NULL);
diff --git a/gsk/gskvulkanrenderer.c b/gsk/gskvulkanrenderer.c
index 4ae0f4deee..7ae536f800 100644
--- a/gsk/gskvulkanrenderer.c
+++ b/gsk/gskvulkanrenderer.c
@@ -44,6 +44,8 @@ struct _GskVulkanRenderer
GSList *textures;
+ GHashTable *glyph_cache;
+
#ifdef G_ENABLE_DEBUG
ProfileTimers profile_timers;
#endif
@@ -343,3 +345,142 @@ gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
return image;
}
+
+#ifndef STACK_BUFFER_SIZE
+#define STACK_BUFFER_SIZE (512 * sizeof (int))
+#endif
+
+#define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T))
+
+static void
+render_text (cairo_t *cr,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ float x,
+ float y,
+ float width,
+ float height)
+{
+ int i, count;
+ int x_position = 0;
+ cairo_scaled_font_t *scaled_font;
+ cairo_glyph_t *cairo_glyphs;
+ cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
+
+ 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, 0, 0, 0, 1);
+
+ if (glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
+ cairo_glyphs = g_new (cairo_glyph_t, glyphs->num_glyphs);
+ else
+ cairo_glyphs = stack_glyphs;
+
+ count = 0;
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = y + (double)(gi->geometry.y_offset) / PANGO_SCALE;
+
+ if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ cairo_glyphs[count].index = gi->glyph;
+ cairo_glyphs[count].x = cx;
+ cairo_glyphs[count].y = cy;
+ count++;
+ }
+ }
+ x_position += gi->geometry.width;
+ }
+
+ cairo_show_glyphs (cr, cairo_glyphs, count);
+
+ if (cairo_glyphs != stack_glyphs)
+ g_free (cairo_glyphs);
+}
+
+GskVulkanImage *
+gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
+ GskVulkanUploader *uploader,
+ PangoFont *font,
+ PangoGlyphString *glyphs)
+{
+ PangoRectangle ink_rect;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ GskVulkanImage *image;
+
+ pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ ink_rect.x + ink_rect.width,
+ ink_rect.height);
+
+ cr = cairo_create (surface);
+ render_text (cr, font, glyphs, 0, - ink_rect.y, ink_rect.x + ink_rect.width, ink_rect.height);
+ cairo_destroy (cr);
+
+ image = gsk_vulkan_image_new_from_data (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));
+ cairo_surface_destroy (surface);
+
+ return image;
+}
+
+static void
+place_text (PangoFont *font,
+ PangoGlyphString *glyphs,
+ GskGlyphCoords *coords,
+ float x,
+ float y,
+ float width,
+ float height)
+{
+ int i;
+ int x_position = 0;
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+
+ if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ coords[i].glyph = gi->glyph;
+ coords[i].x = cx / width;
+ coords[i].y = 0.0;
+ coords[i].width = (float)gi->geometry.width / (PANGO_SCALE * width);
+ coords[i].height = 1.0; // FIXME get actual glyph height
+ }
+ }
+ x_position += gi->geometry.width;
+ }
+}
+
+void
+gsk_vulkan_renderer_get_glyph_coords (GskVulkanRenderer *self,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ GskGlyphCoords *coords)
+{
+ PangoRectangle ink_rect;
+
+ pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ place_text (font, glyphs, coords, 0, - ink_rect.y, ink_rect.x + ink_rect.width, ink_rect.height);
+}
diff --git a/gsk/gskvulkanrendererprivate.h b/gsk/gskvulkanrendererprivate.h
index 5794ba1d53..8e9aefd6f1 100644
--- a/gsk/gskvulkanrendererprivate.h
+++ b/gsk/gskvulkanrendererprivate.h
@@ -25,6 +25,20 @@ GskVulkanImage * gsk_vulkan_renderer_ref_texture_image (GskVulk
GskTexture *texture,
GskVulkanUploader *uploader);
+typedef struct {
+ PangoGlyph glyph;
+ float x, y, width, height;
+} GskGlyphCoords;
+
+GskVulkanImage * gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
+ GskVulkanUploader *uploader,
+ PangoFont *font,
+ PangoGlyphString *glyphs);
+void gsk_vulkan_renderer_get_glyph_coords (GskVulkanRenderer *self,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ GskGlyphCoords *coords);
+
G_END_DECLS
#endif /* __GSK_VULKAN_RENDERER_PRIVATE_H__ */
diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c
index f3b2b0cb92..0752b85f05 100644
--- a/gsk/gskvulkanrenderpass.c
+++ b/gsk/gskvulkanrenderpass.c
@@ -12,11 +12,16 @@
#include "gskvulkanboxshadowpipelineprivate.h"
#include "gskvulkanclipprivate.h"
#include "gskvulkancolorpipelineprivate.h"
+#include "gskvulkancolortextpipelineprivate.h"
#include "gskvulkaneffectpipelineprivate.h"
#include "gskvulkanlineargradientpipelineprivate.h"
+#include "gskvulkantextpipelineprivate.h"
#include "gskvulkanimageprivate.h"
#include "gskvulkanpushconstantsprivate.h"
#include "gskvulkanrendererprivate.h"
+#include "gskprivate.h"
+
+#include <cairo-ft.h>
typedef union _GskVulkanOp GskVulkanOp;
typedef struct _GskVulkanOpRender GskVulkanOpRender;
@@ -29,6 +34,8 @@ typedef enum {
GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP,
GSK_VULKAN_OP_SURFACE,
GSK_VULKAN_OP_TEXTURE,
+ GSK_VULKAN_OP_TEXT,
+ GSK_VULKAN_OP_COLOR_TEXT,
GSK_VULKAN_OP_COLOR,
GSK_VULKAN_OP_LINEAR_GRADIENT,
GSK_VULKAN_OP_OPACITY,
@@ -95,6 +102,23 @@ gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
g_slice_free (GskVulkanRenderPass, self);
}
+static gboolean
+font_has_color_glyphs (PangoFont *font)
+{
+ cairo_scaled_font_t *scaled_font;
+ gboolean has_color = FALSE;
+
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
+ {
+ FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
+ has_color = (FT_HAS_COLOR (ft_face) != 0);
+ cairo_ft_scaled_font_unlock_face (scaled_font);
+ }
+
+ return has_color;
+}
+
#define FALLBACK(...) G_STMT_START { \
GSK_NOTE (FALLBACK, g_print (__VA_ARGS__)); \
goto fallback; \
@@ -172,6 +196,35 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
g_array_append_val (self->render_ops, op);
return;
+ case GSK_TEXT_NODE:
+ 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;
+
case GSK_TEXTURE_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BLEND;
@@ -528,7 +581,9 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_SURFACE:
{
- cairo_surface_t *surface = gsk_cairo_node_get_surface (op->render.node);
+ cairo_surface_t *surface;
+
+ surface = gsk_cairo_node_get_surface (op->render.node);
op->render.source = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),
@@ -538,6 +593,17 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
}
break;
+ 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);
+ }
+ break;
+
case GSK_VULKAN_OP_TEXTURE:
{
op->render.source = gsk_vulkan_renderer_ref_texture_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
@@ -619,6 +685,18 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
n_bytes += op->render.vertex_count;
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;
+ 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;
+ break;
+
case GSK_VULKAN_OP_COLOR:
op->render.vertex_count = gsk_vulkan_color_pipeline_count_vertex_data (GSK_VULKAN_COLOR_PIPELINE (op->render.pipeline));
n_bytes += op->render.vertex_count;
@@ -663,6 +741,7 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
gsize
gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
guchar *data,
gsize offset,
gsize total)
@@ -692,6 +771,37 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
}
break;
+ 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),
+ 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;
+ }
+ 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),
+ 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;
+ }
+ break;
+
case GSK_VULKAN_OP_COLOR:
{
op->render.vertex_offset = offset + n_bytes;
@@ -834,6 +944,8 @@ 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:
@@ -910,6 +1022,72 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
current_draw_index, 1);
break;
+ case GSK_VULKAN_OP_TEXT:
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_layout_get_pipeline_layout (layout),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.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)));
+ break;
+
+ case GSK_VULKAN_OP_COLOR_TEXT:
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_layout_get_pipeline_layout (layout),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.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)));
+ break;
+
case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_COLOR_MATRIX:
if (current_pipeline != op->render.pipeline)
diff --git a/gsk/gskvulkanrenderpassprivate.h b/gsk/gskvulkanrenderpassprivate.h
index c7afdb5a21..48a2461246 100644
--- a/gsk/gskvulkanrenderpassprivate.h
+++ b/gsk/gskvulkanrenderpassprivate.h
@@ -26,6 +26,7 @@ void gsk_vulkan_render_pass_upload (GskVulk
gsize gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self);
gsize gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
guchar *data,
gsize offset,
gsize total);
diff --git a/gsk/gskvulkanrenderprivate.h b/gsk/gskvulkanrenderprivate.h
index 4c01e82e9a..f372282d25 100644
--- a/gsk/gskvulkanrenderprivate.h
+++ b/gsk/gskvulkanrenderprivate.h
@@ -34,6 +34,12 @@ typedef enum {
GSK_VULKAN_PIPELINE_BLUR,
GSK_VULKAN_PIPELINE_BLUR_CLIP,
GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_TEXT,
+ GSK_VULKAN_PIPELINE_TEXT_CLIP,
+ GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED,
/* add more */
GSK_VULKAN_N_PIPELINES
} GskVulkanPipelineType;
diff --git a/gsk/gskvulkantextpipeline.c b/gsk/gskvulkantextpipeline.c
new file mode 100644
index 0000000000..430a2310e8
--- /dev/null
+++ b/gsk/gskvulkantextpipeline.c
@@ -0,0 +1,173 @@
+#include "config.h"
+
+#include "gskvulkantextpipelineprivate.h"
+
+struct _GskVulkanTextPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanTextInstance GskVulkanTextInstance;
+
+struct _GskVulkanTextInstance
+{
+ float rect[4];
+ float tex_rect[4];
+ float color[4];
+};
+
+G_DEFINE_TYPE (GskVulkanTextPipeline, gsk_vulkan_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanTextInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, color),
+ }
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_text_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanTextPipeline *self = GSK_VULKAN_TEXT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_text_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_text_pipeline_class_init (GskVulkanTextPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_text_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_text_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_text_pipeline_init (GskVulkanTextPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_text_pipeline_new (GskVulkanPipelineLayout *layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new_full (GSK_TYPE_VULKAN_TEXT_PIPELINE, layout, shader_name, render_pass,
+ VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+gsize
+gsk_vulkan_text_pipeline_count_vertex_data (GskVulkanTextPipeline *pipeline,
+ int num_instances)
+{
+ return sizeof (GskVulkanTextInstance) * num_instances;
+}
+
+void
+gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ const GdkRGBA *color,
+ float x,
+ float y)
+{
+ GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
+ int i, count;
+ int x_position = 0;
+ float ink_rect_y;
+ float ink_rect_height;
+ GskGlyphCoords *coords;
+
+ /* XXX */
+ ink_rect_y = rect->origin.y - y;
+ ink_rect_height = rect->size.height;
+
+ coords = g_new (GskGlyphCoords, glyphs->num_glyphs);
+ gsk_vulkan_renderer_get_glyph_coords (renderer, font, glyphs, coords);
+
+ count = 0;
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
+
+ if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ GskVulkanTextInstance *instance = &instances[count];
+
+ instance->rect[0] = x + cx;
+ instance->rect[1] = y + ink_rect_y + cy;
+ instance->rect[2] = (float)gi->geometry.width / PANGO_SCALE;
+ instance->rect[3] = ink_rect_height;
+ instance->tex_rect[0] = coords[i].x;
+ instance->tex_rect[1] = coords[i].y;
+ instance->tex_rect[2] = coords[i].width;
+ instance->tex_rect[3] = coords[i].height;
+ instance->color[0] = color->red;
+ instance->color[1] = color->green;
+ instance->color[2] = color->blue;
+ instance->color[3] = color->alpha;
+
+ count++;
+ }
+ }
+ x_position += gi->geometry.width;
+ }
+
+ g_free (coords);
+}
+
+gsize
+gsk_vulkan_text_pipeline_draw (GskVulkanTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/gskvulkantextpipelineprivate.h b/gsk/gskvulkantextpipelineprivate.h
new file mode 100644
index 0000000000..a41846d57a
--- /dev/null
+++ b/gsk/gskvulkantextpipelineprivate.h
@@ -0,0 +1,39 @@
+#ifndef __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrendererprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanTextPipelineLayout GskVulkanTextPipelineLayout;
+
+#define GSK_TYPE_VULKAN_TEXT_PIPELINE (gsk_vulkan_text_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanTextPipeline, gsk_vulkan_text_pipeline, GSK, VULKAN_TEXT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_text_pipeline_new (GskVulkanPipelineLayout * layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_text_pipeline_count_vertex_data (GskVulkanTextPipeline *pipeline,
+ int num_instances);
+void gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ const GdkRGBA *color,
+ float x,
+ float y);
+gsize gsk_vulkan_text_pipeline_draw (GskVulkanTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/meson.build b/gsk/meson.build
index 3c1278f12d..635361532f 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -61,10 +61,12 @@ if have_vulkan
'gskvulkanbuffer.c',
'gskvulkanclip.c',
'gskvulkancolorpipeline.c',
+ 'gskvulkancolortextpipeline.c',
'gskvulkancommandpool.c',
'gskvulkaneffectpipeline.c',
'gskvulkanlineargradientpipeline.c',
'gskvulkanimage.c',
+ 'gskvulkantextpipeline.c',
'gskvulkanmemory.c',
'gskvulkanpipeline.c',
'gskvulkanpushconstants.c',
diff --git a/gsk/resources/vulkan/mask-clip-rounded.frag.spv b/gsk/resources/vulkan/mask-clip-rounded.frag.spv
new file mode 100644
index 0000000000..0791cb599b
--- /dev/null
+++ b/gsk/resources/vulkan/mask-clip-rounded.frag.spv
Binary files differ
diff --git a/gsk/resources/vulkan/mask-clip-rounded.vert.spv b/gsk/resources/vulkan/mask-clip-rounded.vert.spv
new file mode 100644
index 0000000000..34156b4535
--- /dev/null
+++ b/gsk/resources/vulkan/mask-clip-rounded.vert.spv
Binary files differ
diff --git a/gsk/resources/vulkan/mask-clip.frag.spv b/gsk/resources/vulkan/mask-clip.frag.spv
new file mode 100644
index 0000000000..85fc5f01d5
--- /dev/null
+++ b/gsk/resources/vulkan/mask-clip.frag.spv
Binary files differ
diff --git a/gsk/resources/vulkan/mask-clip.vert.spv b/gsk/resources/vulkan/mask-clip.vert.spv
new file mode 100644
index 0000000000..34156b4535
--- /dev/null
+++ b/gsk/resources/vulkan/mask-clip.vert.spv
Binary files differ
diff --git a/gsk/resources/vulkan/mask.frag b/gsk/resources/vulkan/mask.frag
new file mode 100644
index 0000000000..f713371bc6
--- /dev/null
+++ b/gsk/resources/vulkan/mask.frag
@@ -0,0 +1,16 @@
+#version 420 core
+
+#include "clip.frag.glsl"
+
+layout(location = 0) in vec2 inPos;
+layout(location = 1) in vec2 inTexCoord;
+layout(location = 2) in vec4 inColor;
+
+layout(set = 0, binding = 0) uniform sampler2D inTexture;
+
+layout(location = 0) out vec4 color;
+
+void main()
+{
+ color = clip (inPos, vec4(inColor.rgb, texture(inTexture, inTexCoord).a));
+}
diff --git a/gsk/resources/vulkan/mask.frag.spv b/gsk/resources/vulkan/mask.frag.spv
new file mode 100644
index 0000000000..85fc5f01d5
--- /dev/null
+++ b/gsk/resources/vulkan/mask.frag.spv
Binary files differ
diff --git a/gsk/resources/vulkan/mask.vert b/gsk/resources/vulkan/mask.vert
new file mode 100644
index 0000000000..f4bd9c18e3
--- /dev/null
+++ b/gsk/resources/vulkan/mask.vert
@@ -0,0 +1,38 @@
+#version 420 core
+
+#include "clip.vert.glsl"
+
+layout(location = 0) in vec4 inRect;
+layout(location = 1) in vec4 inTexRect;
+layout(location = 2) in vec4 inColor;
+
+layout(location = 0) out vec2 outPos;
+layout(location = 1) out vec2 outTexCoord;
+layout(location = 2) out flat vec4 outColor;
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+vec2 offsets[6] = { vec2(0.0, 0.0),
+ vec2(1.0, 0.0),
+ vec2(0.0, 1.0),
+ vec2(0.0, 1.0),
+ vec2(1.0, 0.0),
+ vec2(1.0, 1.0) };
+
+void main() {
+ vec4 rect = clip (inRect);
+ vec2 pos = rect.xy + rect.zw * offsets[gl_VertexIndex];
+ gl_Position = push.mvp * vec4 (pos, 0.0, 1.0);
+
+ outPos = pos;
+
+ vec4 texrect = vec4((rect.xy - inRect.xy) / inRect.zw,
+ rect.zw / inRect.zw);
+ texrect = vec4(inTexRect.xy + inTexRect.zw * texrect.xy,
+ inTexRect.zw * texrect.zw);
+ outTexCoord = texrect.xy + texrect.zw * offsets[gl_VertexIndex];
+
+ outColor = inColor;
+}
diff --git a/gsk/resources/vulkan/mask.vert.spv b/gsk/resources/vulkan/mask.vert.spv
new file mode 100644
index 0000000000..22cd856995
--- /dev/null
+++ b/gsk/resources/vulkan/mask.vert.spv
Binary files differ
diff --git a/gsk/resources/vulkan/meson.build b/gsk/resources/vulkan/meson.build
index b18a298aa8..1007858525 100644
--- a/gsk/resources/vulkan/meson.build
+++ b/gsk/resources/vulkan/meson.build
@@ -14,6 +14,7 @@ gsk_private_vulkan_fragment_shaders = [
'color-matrix.frag',
'inset-shadow.frag',
'linear.frag',
+ 'mask.frag',
'outset-shadow.frag',
]
@@ -25,6 +26,7 @@ gsk_private_vulkan_vertex_shaders = [
'color-matrix.vert',
'inset-shadow.vert',
'linear.vert',
+ 'mask.vert',
'outset-shadow.vert',
]