[gtk+] More work on text nodes



commit b0e8d8483d2b46dfc4d678e4a217d540837a2478
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Sep 1 16:58:42 2017 -0400

    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.

 gsk/gskprivate.c                                |   20 +++
 gsk/gskprivate.h                                |    3 +
 gsk/gskrendernode.h                             |    6 +-
 gsk/gskrendernodeimpl.c                         |  154 ++++++++++---------
 gsk/gskrendernodeprivate.h                      |    6 +
 gsk/gskvulkancolortextpipeline.c                |  161 ++++++++++++++++++++
 gsk/gskvulkancolortextpipelineprivate.h         |   38 +++++
 gsk/gskvulkanpipeline.c                         |   21 ++-
 gsk/gskvulkanpipelineprivate.h                  |    6 +
 gsk/gskvulkanrender.c                           |   10 +-
 gsk/gskvulkanrenderer.c                         |  141 ++++++++++++++++++
 gsk/gskvulkanrendererprivate.h                  |   14 ++
 gsk/gskvulkanrenderpass.c                       |  180 ++++++++++++++++++++++-
 gsk/gskvulkanrenderpassprivate.h                |    1 +
 gsk/gskvulkanrenderprivate.h                    |    6 +
 gsk/gskvulkantextpipeline.c                     |  173 ++++++++++++++++++++++
 gsk/gskvulkantextpipelineprivate.h              |   39 +++++
 gsk/meson.build                                 |    2 +
 gsk/resources/vulkan/mask-clip-rounded.frag.spv |  Bin 0 -> 8508 bytes
 gsk/resources/vulkan/mask-clip-rounded.vert.spv |  Bin 0 -> 5016 bytes
 gsk/resources/vulkan/mask-clip.frag.spv         |  Bin 0 -> 1600 bytes
 gsk/resources/vulkan/mask-clip.vert.spv         |  Bin 0 -> 5016 bytes
 gsk/resources/vulkan/mask.frag                  |   16 ++
 gsk/resources/vulkan/mask.frag.spv              |  Bin 0 -> 1600 bytes
 gsk/resources/vulkan/mask.vert                  |   38 +++++
 gsk/resources/vulkan/mask.vert.spv              |  Bin 0 -> 3284 bytes
 gsk/resources/vulkan/meson.build                |    2 +
 gtk/gskpango.c                                  |    8 +-
 28 files changed, 954 insertions(+), 91 deletions(-)
---
diff --git a/gsk/gskprivate.c b/gsk/gskprivate.c
index 8404b9d..d9e1c3e 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 84539c1..8584be6 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 46f6094..3816e1c 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 6ddbb2b..a71bea7 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 be29f87..4934ea3 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 0000000..5e3bafb
--- /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 0000000..b341abd
--- /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 7fff1d2..71ea27e 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 91ffee8..504306a 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 4da8e48..6398829 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 4ae0f4d..7ae536f 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 5794ba1..8e9aefd 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 f3b2b0c..0752b85 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 c7afdb5..48a2461 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 4c01e82..f372282 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 0000000..430a231
--- /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 0000000..a41846d
--- /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 3c1278f..6353615 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 0000000..0791cb5
Binary files /dev/null and b/gsk/resources/vulkan/mask-clip-rounded.frag.spv 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 0000000..34156b4
Binary files /dev/null and b/gsk/resources/vulkan/mask-clip-rounded.vert.spv differ
diff --git a/gsk/resources/vulkan/mask-clip.frag.spv b/gsk/resources/vulkan/mask-clip.frag.spv
new file mode 100644
index 0000000..85fc5f0
Binary files /dev/null and b/gsk/resources/vulkan/mask-clip.frag.spv differ
diff --git a/gsk/resources/vulkan/mask-clip.vert.spv b/gsk/resources/vulkan/mask-clip.vert.spv
new file mode 100644
index 0000000..34156b4
Binary files /dev/null and b/gsk/resources/vulkan/mask-clip.vert.spv differ
diff --git a/gsk/resources/vulkan/mask.frag b/gsk/resources/vulkan/mask.frag
new file mode 100644
index 0000000..f713371
--- /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 0000000..85fc5f0
Binary files /dev/null and b/gsk/resources/vulkan/mask.frag.spv differ
diff --git a/gsk/resources/vulkan/mask.vert b/gsk/resources/vulkan/mask.vert
new file mode 100644
index 0000000..f4bd9c1
--- /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 0000000..22cd856
Binary files /dev/null and b/gsk/resources/vulkan/mask.vert.spv differ
diff --git a/gsk/resources/vulkan/meson.build b/gsk/resources/vulkan/meson.build
index b18a298..1007858 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',
 ]
 
diff --git a/gtk/gskpango.c b/gtk/gskpango.c
index 698688a..ff91d08 100644
--- a/gtk/gskpango.c
+++ b/gtk/gskpango.c
@@ -112,8 +112,6 @@ gsk_pango_renderer_show_text_glyphs (PangoRenderer        *renderer,
                                      int                   y)
 {
   GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
-  double base_x = (double)x / PANGO_SCALE;
-  double base_y = (double)y / PANGO_SCALE;
   int x_offset, y_offset;
   GskRenderNode *node;
   GdkRGBA color;
@@ -121,7 +119,7 @@ gsk_pango_renderer_show_text_glyphs (PangoRenderer        *renderer,
   gtk_snapshot_get_offset (crenderer->snapshot, &x_offset, &y_offset);
   get_color (crenderer, PANGO_RENDER_PART_FOREGROUND, &color);
 
-  node = gsk_text_node_new (font, glyphs, &color, x_offset, y_offset, base_x, base_y);
+  node = gsk_text_node_new (font, glyphs, &color, x_offset + (double)x/PANGO_SCALE, y_offset + 
(double)y/PANGO_SCALE);
   if (node == NULL)
     return;
 
@@ -132,12 +130,8 @@ gsk_pango_renderer_show_text_glyphs (PangoRenderer        *renderer,
       gsk_render_node_set_name (node, name);
     }
 
-  gtk_snapshot_offset (crenderer->snapshot, base_x, base_y);
-
   gtk_snapshot_append_node (crenderer->snapshot, node);
   gsk_render_node_unref (node);
-
-  gtk_snapshot_offset (crenderer->snapshot, -base_x, -base_y);
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]