[gtk+/wip/matthiasc/shadertoy: 1/5] Support custom shaders in vulkan



commit 2905e9dbccf35d0176ba37f2ac404d3f169358d3
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Sep 23 20:38:12 2017 -0400

    Support custom shaders in vulkan
    
    Initial work for letting widgets and eventually applications
    provide their own shaders. For now, we only have a limited set
    of inputs: the rectangle, a time parameter and up to two
    children which are passed as textures to the shader.

 gsk/gskenums.h                                    |    4 +-
 gsk/gskrendernode.h                               |   12 ++
 gsk/gskrendernodeimpl.c                           |  220 ++++++++++++++++++++-
 gsk/gskrendernodeprivate.h                        |    3 +
 gsk/gskvulkancustompipeline.c                     |  171 ++++++++++++++++
 gsk/gskvulkancustompipelineprivate.h              |   35 ++++
 gsk/gskvulkanpipeline.c                           |   39 ++++-
 gsk/gskvulkanpipelineprivate.h                    |    9 +
 gsk/gskvulkanrender.c                             |   20 ++
 gsk/gskvulkanrenderpass.c                         |  103 ++++++++++-
 gsk/gskvulkanrenderprivate.h                      |    2 +
 gsk/gskvulkanshader.c                             |    2 +-
 gsk/gskvulkanshaderprivate.h                      |    4 +
 gsk/meson.build                                   |    1 +
 gsk/resources/vulkan/custom-clip-rounded.vert.spv |  Bin 0 -> 4200 bytes
 gsk/resources/vulkan/custom-clip.vert.spv         |  Bin 0 -> 4200 bytes
 gsk/resources/vulkan/custom.vert                  |   53 +++++
 gsk/resources/vulkan/custom.vert.spv              |  Bin 0 -> 4200 bytes
 gsk/resources/vulkan/meson.build                  |    1 +
 19 files changed, 671 insertions(+), 8 deletions(-)
---
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index ca9d5a8..b4d20be 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -45,6 +45,7 @@
  * @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
  * @GSK_TEXT_NODE: A node containing a glyph string
  * @GSK_BLUR_NODE: A node that applies a blur
+ * @GSK_PIXEL_SHADER_NODE: A node that applies a custom shader
  *
  * The type of a node determines what the node is rendering.
  *
@@ -71,7 +72,8 @@ typedef enum {
   GSK_BLEND_NODE,
   GSK_CROSS_FADE_NODE,
   GSK_TEXT_NODE,
-  GSK_BLUR_NODE
+  GSK_BLUR_NODE,
+  GSK_PIXEL_SHADER_NODE
 } GskRenderNodeType;
 
 /**
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 8c67fd8..42112c8 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -182,6 +182,18 @@ GDK_AVAILABLE_IN_3_92
 GskRenderNode *         gsk_blur_node_new                       (GskRenderNode *child,
                                                                  double         radius);
 
+GDK_AVAILABLE_IN_3_92
+GskRenderNode *         gsk_pixel_shader_node_new               (const graphene_rect_t  *bounds,
+                                                                 GskRenderNode          *child1,
+                                                                 GskRenderNode          *child2,
+                                                                 GBytes                 *fragement_bytes,
+                                                                 float                   time);
+GDK_AVAILABLE_IN_3_92
+GskRenderNode          *gsk_pixel_shader_node_get_child1        (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_92
+GskRenderNode          *gsk_pixel_shader_node_get_child2         (GskRenderNode *node);
+
+
 GDK_AVAILABLE_IN_3_90
 void                    gsk_render_node_set_scaling_filters     (GskRenderNode *node,
                                                                  GskScalingFilter min_filter,
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index d560d8a..e5e1e11 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -4414,6 +4414,223 @@ gsk_blur_node_get_radius (GskRenderNode *node)
   return self->radius;
 }
 
+/*** GSK_PIXEL_SHADER_NODE ***/
+
+typedef struct _GskPixelShaderNode GskPixelShaderNode;
+
+struct _GskPixelShaderNode
+{
+  GskRenderNode render_node;
+
+  GBytes *fragment_bytes;
+
+  float time;
+
+  GskRenderNode *child1;
+  GskRenderNode *child2;
+};
+
+static void
+gsk_pixel_shader_node_finalize (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  g_bytes_unref (self->fragment_bytes);
+
+  if (self->child1)
+    gsk_render_node_unref (self->child1);
+  if (self->child2)
+    gsk_render_node_unref (self->child2);
+}
+
+static void
+gsk_pixel_shader_node_draw (GskRenderNode *node,
+                            cairo_t       *cr)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  cairo_save (cr);
+  cairo_set_source_rgb (cr, 1, 0, 0);
+  cairo_paint (cr);
+  cairo_restore (cr);
+
+  if (self->child1)
+    gsk_render_node_draw (self->child1, cr);
+  if (self->child2)
+    gsk_render_node_draw (self->child2, cr);
+}
+
+#define GSK_PIXEL_SHADER_NODE_VARIANT_TYPE "(dddda(uv)ayd)"
+
+static GVariant *
+gsk_pixel_shader_node_serialize (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE (GSK_CONTAINER_NODE_VARIANT_TYPE));
+
+  if (self->child1)
+    g_variant_builder_add (&builder, "(uv)",
+                           (guint32) gsk_render_node_get_node_type (self->child1),
+                           gsk_render_node_serialize_node (self->child1));
+  if (self->child2)
+    g_variant_builder_add (&builder, "(uv)",
+                           (guint32) gsk_render_node_get_node_type (self->child2),
+                           gsk_render_node_serialize_node (self->child2));
+
+  return g_variant_new ("(dddd@ayda(uv))",
+                        (double) node->bounds.origin.x, (double) node->bounds.origin.y,
+                        (double) node->bounds.size.width, (double) node->bounds.size.height,
+                         &builder,
+                         g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
+                                                    g_bytes_get_data (self->fragment_bytes, NULL),
+                                                    g_bytes_get_size (self->fragment_bytes), 1),
+                         self->time);
+}
+
+static GskRenderNode *
+gsk_pixel_shader_node_deserialize (GVariant  *variant,
+                                   GError   **error)
+{
+  GVariant *fragment_variant;
+  char *data;
+  double bounds[4];
+  double time;
+  gsize length;
+  GBytes *fragment_bytes;
+  GskRenderNode *node;
+  GVariantIter *iter;
+  gsize i, n_children;
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *children[2] = { NULL, NULL };
+
+  if (!check_variant_type (variant, GSK_PIXEL_SHADER_NODE_VARIANT_TYPE, error))
+    return NULL;
+
+  g_variant_get (variant, "(dddda(uv)@ayd)",
+                 &bounds[0], &bounds[1], &bounds[2], &bounds[3],
+                 &iter, &fragment_variant, &time);
+
+  n_children = g_variant_iter_init (iter, variant);
+  if (n_children > 2)
+    return NULL;
+
+  i = 0;
+  while (g_variant_iter_loop (iter, "(uv)", &child_type, &child_variant))
+    {
+      children[i] = gsk_render_node_deserialize_node (child_type, child_variant, error);
+      if (children[i] == NULL)
+        {
+          guint j;
+          for (j = 0; j < i; j++)
+            gsk_render_node_unref (children[j]);
+          g_variant_unref (child_variant);
+          return NULL;
+        }
+      i++;
+    }
+
+  data = g_variant_get_fixed_array (fragment_variant, &length, 1);
+  fragment_bytes = g_bytes_new (data, length);
+
+  node = gsk_pixel_shader_node_new (&GRAPHENE_RECT_INIT(bounds[0], bounds[1], bounds[2], bounds[3]),
+                                    children[0], children[1],
+                                    fragment_bytes, time);
+
+  if (children[0])
+    gsk_render_node_unref (children[0]);
+  if (children[1])
+    gsk_render_node_unref (children[1]);
+
+  g_bytes_unref (fragment_bytes);
+  g_variant_unref (fragment_variant);
+
+  return node;
+}
+
+static const GskRenderNodeClass GSK_PIXEL_SHADER_NODE_CLASS = {
+  GSK_PIXEL_SHADER_NODE,
+  sizeof (GskPixelShaderNode),
+  "GskPixelShaderNode",
+  gsk_pixel_shader_node_finalize,
+  gsk_pixel_shader_node_draw,
+  gsk_pixel_shader_node_serialize,
+  gsk_pixel_shader_node_deserialize
+};
+
+GskRenderNode *
+gsk_pixel_shader_node_new (const graphene_rect_t  *bounds,
+                           GskRenderNode          *child1,
+                           GskRenderNode          *child2,
+                           GBytes                 *fragment_bytes,
+                           float                   time)
+{
+  GskPixelShaderNode *self;
+
+  self = (GskPixelShaderNode *) gsk_render_node_new (&GSK_PIXEL_SHADER_NODE_CLASS, 0);
+
+  if (child1)
+    self->child1 = gsk_render_node_ref (child1);
+  else
+    self->child1 = NULL;
+
+  if (child2)
+    self->child2 = gsk_render_node_ref (child2);
+  else
+    self->child2 = NULL;
+
+  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+
+  self->fragment_bytes = g_bytes_ref (fragment_bytes);
+  self->time = time;
+
+  return &self->render_node;
+}
+
+GBytes *
+gsk_pixel_shader_node_get_fragment_bytes (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), NULL);
+
+  return self->fragment_bytes;
+}
+
+float
+gsk_pixel_shader_node_get_time (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
+
+  return self->time;
+}
+
+GskRenderNode *
+gsk_pixel_shader_node_get_child1 (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
+
+  return self->child1;
+}
+
+GskRenderNode *
+gsk_pixel_shader_node_get_child2 (GskRenderNode *node)
+{
+  GskPixelShaderNode *self = (GskPixelShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
+
+  return self->child2;
+}
+
+/*** ***/
+
 static const GskRenderNodeClass *klasses[] = {
   [GSK_CONTAINER_NODE] = &GSK_CONTAINER_NODE_CLASS,
   [GSK_CAIRO_NODE] = &GSK_CAIRO_NODE_CLASS,
@@ -4434,7 +4651,8 @@ static const GskRenderNodeClass *klasses[] = {
   [GSK_BLEND_NODE] = &GSK_BLEND_NODE_CLASS,
   [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS,
   [GSK_TEXT_NODE] = &GSK_TEXT_NODE_CLASS,
-  [GSK_BLUR_NODE] = &GSK_BLUR_NODE_CLASS
+  [GSK_BLUR_NODE] = &GSK_BLUR_NODE_CLASS,
+  [GSK_PIXEL_SHADER_NODE] = &GSK_PIXEL_SHADER_NODE_CLASS,
 };
 
 GskRenderNode *
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index e7518e4..857fae6 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -110,6 +110,9 @@ double gsk_cross_fade_node_get_progress (GskRenderNode *node);
 GskRenderNode * gsk_blur_node_get_child (GskRenderNode *node);
 double gsk_blur_node_get_radius (GskRenderNode *node);
 
+GBytes * gsk_pixel_shader_node_get_fragment_bytes (GskRenderNode *node);
+float    gsk_pixel_shader_node_get_time (GskRenderNode *node);
+
 G_END_DECLS
 
 #endif /* __GSK_RENDER_NODE_PRIVATE_H__ */
diff --git a/gsk/gskvulkancustompipeline.c b/gsk/gskvulkancustompipeline.c
new file mode 100644
index 0000000..b60b5f9
--- /dev/null
+++ b/gsk/gskvulkancustompipeline.c
@@ -0,0 +1,171 @@
+#include "config.h"
+
+#include "gskvulkancustompipelineprivate.h"
+
+struct _GskVulkanCustomPipeline
+{
+  GObject parent_instance;
+};
+
+typedef struct _GskVulkanCustomInstance GskVulkanCustomInstance;
+
+struct _GskVulkanCustomInstance
+{
+  float rect[4];
+  float tex_rect1[4];
+  float tex_rect2[4];
+  float time;
+};
+
+G_DEFINE_TYPE (GskVulkanCustomPipeline, gsk_vulkan_custom_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_custom_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+  static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+      {
+          .binding = 0,
+          .stride = sizeof (GskVulkanCustomInstance),
+          .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+      }
+  };
+  static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+      {
+          .location = 0,
+          .binding = 0,
+          .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+          .offset = 0,
+      },
+      {
+          .location = 1,
+          .binding = 0,
+          .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+          .offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, tex_rect1),
+      },
+      {
+          .location = 2,
+          .binding = 0,
+          .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+          .offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, tex_rect2),
+      },
+      {
+          .location = 3,
+          .binding = 0,
+          .format = VK_FORMAT_R32_SFLOAT,
+          .offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, time),
+      },
+  };
+  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_custom_pipeline_finalize (GObject *gobject)
+{
+  //GskVulkanCustomPipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
+
+  G_OBJECT_CLASS (gsk_vulkan_custom_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_custom_pipeline_class_init (GskVulkanCustomPipelineClass *klass)
+{
+  GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+  G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_custom_pipeline_finalize;
+
+  pipeline_class->get_input_state_create_info = gsk_vulkan_custom_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_custom_pipeline_init (GskVulkanCustomPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_custom_pipeline_new (GdkVulkanContext      *context,
+                                VkPipelineLayout       layout,
+                                GBytes                *fragment_bytes,
+                                VkRenderPass           render_pass)
+{
+  GskVulkanShader *vertex_shader;
+  GskVulkanShader *fragment_shader;
+  GError *error = NULL;
+
+  vertex_shader = gsk_vulkan_shader_new_from_resource (context,
+                                                       GSK_VULKAN_SHADER_VERTEX,
+                                                       "custom",
+                                                       &error);
+  fragment_shader = gsk_vulkan_shader_new_from_bytes (context,
+                                                      GSK_VULKAN_SHADER_FRAGMENT,
+                                                      fragment_bytes,
+                                                      &error);
+  if (fragment_shader == NULL)
+    {
+      g_error ("%s", error->message);
+      g_error_free (error);
+      return NULL;
+    }
+
+  return gsk_vulkan_pipeline_new_with_shaders (GSK_TYPE_VULKAN_CUSTOM_PIPELINE,
+                                               context, layout,
+                                               vertex_shader,
+                                               fragment_shader,
+                                               render_pass,
+                                               VK_BLEND_FACTOR_ONE,
+                                               VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+gsize
+gsk_vulkan_custom_pipeline_count_vertex_data (GskVulkanCustomPipeline *pipeline)
+{
+  return sizeof (GskVulkanCustomInstance);
+}
+
+void
+gsk_vulkan_custom_pipeline_collect_vertex_data (GskVulkanCustomPipeline *pipeline,
+                                                guchar                  *data,
+                                                const graphene_rect_t   *bounds,
+                                                const graphene_rect_t   *tex_rect1,
+                                                const graphene_rect_t   *tex_rect2,
+                                                float                    time)
+{
+  GskVulkanCustomInstance *instance = (GskVulkanCustomInstance *) data;
+
+  instance->rect[0] = bounds->origin.x;
+  instance->rect[1] = bounds->origin.y;
+  instance->rect[2] = bounds->size.width;
+  instance->rect[3] = bounds->size.height;
+
+  instance->tex_rect1[0] = tex_rect1->origin.x;
+  instance->tex_rect1[1] = tex_rect1->origin.y;
+  instance->tex_rect1[2] = tex_rect1->size.width;
+  instance->tex_rect1[3] = tex_rect1->size.height;
+
+  instance->tex_rect2[0] = tex_rect2->origin.x;
+  instance->tex_rect2[1] = tex_rect2->origin.y;
+  instance->tex_rect2[2] = tex_rect2->size.width;
+  instance->tex_rect2[3] = tex_rect2->size.height;
+
+  instance->time = time;
+}
+
+gsize
+gsk_vulkan_custom_pipeline_draw (GskVulkanCustomPipeline *pipeline,
+                                 VkCommandBuffer          command_buffer,
+                                 gsize                    offset,
+                                 gsize                    n_commands)
+{
+  vkCmdDraw (command_buffer,
+             6, n_commands,
+             0, offset);
+
+  return n_commands;
+}
diff --git a/gsk/gskvulkancustompipelineprivate.h b/gsk/gskvulkancustompipelineprivate.h
new file mode 100644
index 0000000..dc2f0b2
--- /dev/null
+++ b/gsk/gskvulkancustompipelineprivate.h
@@ -0,0 +1,35 @@
+#ifndef __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanCustomPipelineLayout GskVulkanCustomPipelineLayout;
+
+#define GSK_TYPE_VULKAN_CUSTOM_PIPELINE (gsk_vulkan_custom_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanCustomPipeline, gsk_vulkan_custom_pipeline, GSK, VULKAN_CUSTOM_PIPELINE, 
GskVulkanPipeline)
+
+GskVulkanPipeline *     gsk_vulkan_custom_pipeline_new                   (GdkVulkanContext        *context,
+                                                                          VkPipelineLayout         layout,
+                                                                          GBytes                  
*fragment_shader,
+                                                                          VkRenderPass             
render_pass);
+
+gsize                   gsk_vulkan_custom_pipeline_count_vertex_data     (GskVulkanCustomPipeline   
*pipeline);
+void                    gsk_vulkan_custom_pipeline_collect_vertex_data   (GskVulkanCustomPipeline   
*pipeline,
+                                                                          guchar                  *data,
+                                                                          const graphene_rect_t   *rect,
+                                                                          const graphene_rect_t   
*child1_bounds,
+                                                                          const graphene_rect_t   
*child2_bounds,
+                                                                          float                    time);
+gsize                   gsk_vulkan_custom_pipeline_draw                  (GskVulkanCustomPipeline   
*pipeline,
+                                                                          VkCommandBuffer          
command_buffer,
+                                                                          gsize                    offset,
+                                                                          gsize                    
n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/gskvulkanpipeline.c b/gsk/gskvulkanpipeline.c
index 38d5a57..341c683 100644
--- a/gsk/gskvulkanpipeline.c
+++ b/gsk/gskvulkanpipeline.c
@@ -60,7 +60,9 @@ gsk_vulkan_pipeline_new (GType                    pipeline_type,
                          const char              *shader_name,
                          VkRenderPass             render_pass)
 {
-  return gsk_vulkan_pipeline_new_full (pipeline_type, context, layout, shader_name, render_pass,
+  return gsk_vulkan_pipeline_new_full (pipeline_type, context, layout,
+                                       shader_name,
+                                       render_pass,
                                        VK_BLEND_FACTOR_ONE,
                                        VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
 }
@@ -74,13 +76,42 @@ gsk_vulkan_pipeline_new_full (GType                    pipeline_type,
                               VkBlendFactor            srcBlendFactor,
                               VkBlendFactor            dstBlendFactor)
 {
+  GskVulkanShader *vertex_shader;
+  GskVulkanShader *fragment_shader;
+
+  vertex_shader = gsk_vulkan_shader_new_from_resource (context,
+                                                       GSK_VULKAN_SHADER_VERTEX,
+                                                       shader_name,
+                                                       NULL);
+  fragment_shader = gsk_vulkan_shader_new_from_resource (context,
+                                                         GSK_VULKAN_SHADER_FRAGMENT,
+                                                         shader_name,
+                                                         NULL);
+
+  return gsk_vulkan_pipeline_new_with_shaders (pipeline_type, context, layout,
+                                               vertex_shader, fragment_shader,
+                                               render_pass,
+                                               srcBlendFactor, dstBlendFactor);
+}
+
+GskVulkanPipeline *
+gsk_vulkan_pipeline_new_with_shaders (GType                    pipeline_type,
+                                      GdkVulkanContext        *context,
+                                      VkPipelineLayout         layout,
+                                      GskVulkanShader         *vertex_shader,
+                                      GskVulkanShader         *fragment_shader,
+                                      VkRenderPass             render_pass,
+                                      VkBlendFactor            srcBlendFactor,
+                                      VkBlendFactor            dstBlendFactor)
+{
   GskVulkanPipelinePrivate *priv;
   GskVulkanPipeline *self;
   VkDevice device;
 
   g_return_val_if_fail (g_type_is_a (pipeline_type, GSK_TYPE_VULKAN_PIPELINE), NULL);
   g_return_val_if_fail (layout != VK_NULL_HANDLE, NULL);
-  g_return_val_if_fail (shader_name != NULL, NULL);
+  g_return_val_if_fail (vertex_shader != NULL, NULL);
+  g_return_val_if_fail (fragment_shader != NULL, NULL);
   g_return_val_if_fail (render_pass != VK_NULL_HANDLE, NULL);
 
   self = g_object_new (pipeline_type, NULL);
@@ -92,8 +123,8 @@ gsk_vulkan_pipeline_new_full (GType                    pipeline_type,
   priv->context = context;
   priv->layout = layout;
 
-  priv->vertex_shader = gsk_vulkan_shader_new_from_resource (context, GSK_VULKAN_SHADER_VERTEX, shader_name, 
NULL);
-  priv->fragment_shader = gsk_vulkan_shader_new_from_resource (context, GSK_VULKAN_SHADER_FRAGMENT, 
shader_name, NULL);
+  priv->vertex_shader = vertex_shader;
+  priv->fragment_shader = fragment_shader;
 
   GSK_VK_CHECK (vkCreateGraphicsPipelines, device,
                                            VK_NULL_HANDLE,
diff --git a/gsk/gskvulkanpipelineprivate.h b/gsk/gskvulkanpipelineprivate.h
index cd41f82..6d03b54 100644
--- a/gsk/gskvulkanpipelineprivate.h
+++ b/gsk/gskvulkanpipelineprivate.h
@@ -4,6 +4,7 @@
 #include <gdk/gdk.h>
 
 #include "gskdebugprivate.h"
+#include "gskvulkanshaderprivate.h"
 
 G_BEGIN_DECLS
 
@@ -44,6 +45,14 @@ GskVulkanPipeline *     gsk_vulkan_pipeline_new_full                    (GType
                                                                          VkRenderPass                    
render_pass,
                                                                          VkBlendFactor                   
srcBlendFactor,
                                                                          VkBlendFactor                   
dstBlendFactor);
+GskVulkanPipeline *     gsk_vulkan_pipeline_new_with_shaders            (GType                           
pipeline_type,
+                                                                         GdkVulkanContext               
*context,
+                                                                         VkPipelineLayout                
layout,
+                                                                         GskVulkanShader                
*vertex_shader,
+                                                                         GskVulkanShader                
*fragment_shader,
+                                                                         VkRenderPass                    
render_pass,
+                                                                         VkBlendFactor                   
srcBlendFactor,
+                                                                         VkBlendFactor                   
dstBlendFactor);
 
 VkPipeline              gsk_vulkan_pipeline_get_pipeline                (GskVulkanPipeline              
*self);
 VkPipelineLayout        gsk_vulkan_pipeline_get_pipeline_layout         (GskVulkanPipeline              
*self);
diff --git a/gsk/gskvulkanrender.c b/gsk/gskvulkanrender.c
index ad4d164..267ac7f 100644
--- a/gsk/gskvulkanrender.c
+++ b/gsk/gskvulkanrender.c
@@ -17,6 +17,7 @@
 #include "gskvulkancolorpipelineprivate.h"
 #include "gskvulkancolortextpipelineprivate.h"
 #include "gskvulkancrossfadepipelineprivate.h"
+#include "gskvulkancustompipelineprivate.h"
 #include "gskvulkaneffectpipelineprivate.h"
 #include "gskvulkanlineargradientpipelineprivate.h"
 #include "gskvulkantextpipelineprivate.h"
@@ -57,6 +58,7 @@ struct _GskVulkanRender
 
   GList *render_passes;
   GSList *cleanup_images;
+  GSList *cleanup_pipelines;
 
   GQuark render_pass_counter;
   GQuark gpu_time_timer;
@@ -419,6 +421,22 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender       *self,
   return self->pipelines[type];
 }
 
+GskVulkanPipeline *
+gsk_vulkan_render_get_custom_pipeline (GskVulkanRender *self,
+                                       GBytes          *fragment_bytes)
+{
+  GskVulkanPipeline *pipeline;
+
+  pipeline = gsk_vulkan_custom_pipeline_new (self->vulkan,
+                                             self->pipeline_layout[2],
+                                             fragment_bytes,
+                                             self->render_pass);
+
+  self->cleanup_pipelines = g_slist_prepend (self->cleanup_pipelines, pipeline);
+
+  return pipeline;
+}
+
 VkDescriptorSet
 gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self,
                                       gsize            id)
@@ -666,6 +684,8 @@ gsk_vulkan_render_cleanup (GskVulkanRender *self)
   self->render_passes = NULL;
   g_slist_free_full (self->cleanup_images, g_object_unref);
   self->cleanup_images = NULL;
+  g_slist_free_full (self->cleanup_pipelines, g_object_unref);
+  self->cleanup_pipelines = NULL;
 
   g_clear_pointer (&self->clip, cairo_region_destroy);
   g_clear_object (&self->target);
diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c
index 770ebdb..6bfa000 100644
--- a/gsk/gskvulkanrenderpass.c
+++ b/gsk/gskvulkanrenderpass.c
@@ -16,6 +16,7 @@
 #include "gskvulkancolorpipelineprivate.h"
 #include "gskvulkancolortextpipelineprivate.h"
 #include "gskvulkancrossfadepipelineprivate.h"
+#include "gskvulkancustompipelineprivate.h"
 #include "gskvulkaneffectpipelineprivate.h"
 #include "gskvulkanlineargradientpipelineprivate.h"
 #include "gskvulkantextpipelineprivate.h"
@@ -53,6 +54,7 @@ typedef enum {
   GSK_VULKAN_OP_REPEAT,
   GSK_VULKAN_OP_CROSS_FADE,
   GSK_VULKAN_OP_BLEND_MODE,
+  GSK_VULKAN_OP_PIXEL_SHADER,
   /* GskVulkanOpText */
   GSK_VULKAN_OP_TEXT,
   GSK_VULKAN_OP_COLOR_TEXT,
@@ -278,6 +280,13 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
     default:
       FALLBACK ("Unsupported node '%s'\n", node->node_class->type_name);
 
+    case GSK_PIXEL_SHADER_NODE:
+      op.type = GSK_VULKAN_OP_PIXEL_SHADER;
+      op.render.pipeline = gsk_vulkan_render_get_custom_pipeline (render,
+                                                                  gsk_pixel_shader_node_get_fragment_bytes 
(node));
+      g_array_append_val (self->render_ops, op);
+      return;
+
     case GSK_REPEAT_NODE:
       if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
         pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
@@ -304,7 +313,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       op.type = GSK_VULKAN_OP_BLEND_MODE;
       op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
       g_array_append_val (self->render_ops, op);
-       return;
+      return;
 
     case GSK_CROSS_FADE_NODE:
       if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
@@ -1040,6 +1049,34 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass  *self,
           }
           break;
 
+        case GSK_VULKAN_OP_PIXEL_SHADER:
+          {
+            GskRenderNode *child1 = gsk_pixel_shader_node_get_child1 (op->render.node);
+            GskRenderNode *child2 = gsk_pixel_shader_node_get_child2 (op->render.node);
+
+            if (child1)
+              op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+                                                                              render,
+                                                                              uploader,
+                                                                              child1,
+                                                                              &child1->bounds,
+                                                                              clip,
+                                                                              &op->render.source_rect);
+            else
+              op->render.source = NULL;
+            if (child2)
+              op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
+                                                                               render,
+                                                                               uploader,
+                                                                               child2,
+                                                                               &child2->bounds,
+                                                                               clip,
+                                                                               &op->render.source_rect);
+            else
+              op->render.source2 = NULL;
+          }
+          break;
+
         case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
           clip = &op->constants.constants.clip;
           break;
@@ -1134,6 +1171,11 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
           n_bytes += op->render.vertex_count;
           break;
 
+        case GSK_VULKAN_OP_PIXEL_SHADER:
+          op->render.vertex_count = gsk_vulkan_custom_pipeline_count_vertex_data (GSK_VULKAN_CUSTOM_PIPELINE 
(op->render.pipeline));
+          n_bytes += op->render.vertex_count;
+          break;
+
         default:
           g_assert_not_reached ();
 
@@ -1368,6 +1410,19 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
           }
           break;
 
+        case GSK_VULKAN_OP_PIXEL_SHADER:
+          {
+            op->render.vertex_offset = offset + n_bytes;
+            gsk_vulkan_custom_pipeline_collect_vertex_data (GSK_VULKAN_CUSTOM_PIPELINE (op->render.pipeline),
+                                                            data + n_bytes + offset,
+                                                            &op->render.node->bounds,
+                                                            &op->render.source ? &op->render.source_rect : 
&GRAPHENE_RECT_INIT(0,0,1,1),
+                                                            &op->render.source2 ? &op->render.source2_rect : 
&GRAPHENE_RECT_INIT(0,0,1,1),
+                                                            gsk_pixel_shader_node_get_time 
(op->render.node));
+            n_bytes += op->render.vertex_count;
+          }
+          break;
+
         default:
           g_assert_not_reached ();
         case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
@@ -1445,6 +1500,13 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
             op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, 
op->render.source, TRUE);
           break;
 
+        case GSK_VULKAN_OP_PIXEL_SHADER:
+          if (op->render.source)
+            op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, 
op->render.source, FALSE);
+          if (op->render.source2)
+            op->render.descriptor_set_index2 = gsk_vulkan_render_reserve_descriptor_set (render, 
op->render.source2, FALSE);
+          break;
+
         case GSK_VULKAN_OP_TEXT:
         case GSK_VULKAN_OP_COLOR_TEXT:
           op->text.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->text.source, 
FALSE);
@@ -1844,6 +1906,45 @@ gsk_vulkan_render_pass_draw_rect (GskVulkanRenderPass     *self,
                                                                      current_draw_index, 1);
           break;
 
+        case GSK_VULKAN_OP_PIXEL_SHADER:
+          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;
+            }
+
+          {
+            VkDescriptorSet ds[2];
+            gsize size = 0;
+            if (op->render.source != NULL)
+              ds[size++] = gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index);
+            if (op->render.source2 != NULL)
+              ds[size++] = gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2);
+            vkCmdBindDescriptorSets (command_buffer,
+                                     VK_PIPELINE_BIND_POINT_GRAPHICS,
+                                     gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+                                     0,
+                                     size,
+                                     ds,
+                                     0,
+                                     NULL);
+          }
+
+          current_draw_index += gsk_vulkan_custom_pipeline_draw (GSK_VULKAN_CUSTOM_PIPELINE 
(current_pipeline),
+                                                                 command_buffer,
+                                                                 current_draw_index, 1);
+          break;
+
         default:
           g_assert_not_reached ();
           break;
diff --git a/gsk/gskvulkanrenderprivate.h b/gsk/gskvulkanrenderprivate.h
index 1455bb7..10e6540 100644
--- a/gsk/gskvulkanrenderprivate.h
+++ b/gsk/gskvulkanrenderprivate.h
@@ -76,6 +76,8 @@ void                    gsk_vulkan_render_upload                        (GskVulk
 
 GskVulkanPipeline *     gsk_vulkan_render_get_pipeline                  (GskVulkanRender        *self,
                                                                          GskVulkanPipelineType   
pipeline_type);
+GskVulkanPipeline *     gsk_vulkan_render_get_custom_pipeline           (GskVulkanRender        *self,
+                                                                         GBytes                 
*fragment_bytes);
 VkDescriptorSet         gsk_vulkan_render_get_descriptor_set            (GskVulkanRender        *self,
                                                                          gsize                   id);
 gsize                   gsk_vulkan_render_reserve_descriptor_set        (GskVulkanRender        *self,
diff --git a/gsk/gskvulkanshader.c b/gsk/gskvulkanshader.c
index 1010b21..7b9d2be 100644
--- a/gsk/gskvulkanshader.c
+++ b/gsk/gskvulkanshader.c
@@ -11,7 +11,7 @@ struct _GskVulkanShader
   VkShaderModule vk_shader;
 };
 
-static GskVulkanShader *
+GskVulkanShader *
 gsk_vulkan_shader_new_from_bytes (GdkVulkanContext     *context,
                                   GskVulkanShaderType   type,
                                   GBytes               *bytes,
diff --git a/gsk/gskvulkanshaderprivate.h b/gsk/gskvulkanshaderprivate.h
index e94424f..11841af 100644
--- a/gsk/gskvulkanshaderprivate.h
+++ b/gsk/gskvulkanshaderprivate.h
@@ -20,6 +20,10 @@ typedef struct _GskVulkanShader GskVulkanShader;
   .pName = "main", \
 }
 
+GskVulkanShader *       gsk_vulkan_shader_new_from_bytes                (GdkVulkanContext       *context,
+                                                                         GskVulkanShaderType     type,
+                                                                         GBytes                 *bytes,
+                                                                         GError                **error);
 GskVulkanShader *       gsk_vulkan_shader_new_from_resource             (GdkVulkanContext       *context,
                                                                          GskVulkanShaderType     type,
                                                                          const char             
*resource_name,
diff --git a/gsk/meson.build b/gsk/meson.build
index a86e156..e7c9e0b 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -63,6 +63,7 @@ if have_vulkan
     'gskvulkancolorpipeline.c',
     'gskvulkancolortextpipeline.c',
     'gskvulkancrossfadepipeline.c',
+    'gskvulkancustompipeline.c',
     'gskvulkancommandpool.c',
     'gskvulkaneffectpipeline.c',
     'gskvulkanglyphcache.c',
diff --git a/gsk/resources/vulkan/custom-clip-rounded.vert.spv 
b/gsk/resources/vulkan/custom-clip-rounded.vert.spv
new file mode 100644
index 0000000..c7f626b
Binary files /dev/null and b/gsk/resources/vulkan/custom-clip-rounded.vert.spv differ
diff --git a/gsk/resources/vulkan/custom-clip.vert.spv b/gsk/resources/vulkan/custom-clip.vert.spv
new file mode 100644
index 0000000..c7f626b
Binary files /dev/null and b/gsk/resources/vulkan/custom-clip.vert.spv differ
diff --git a/gsk/resources/vulkan/custom.vert b/gsk/resources/vulkan/custom.vert
new file mode 100644
index 0000000..c36bb47
--- /dev/null
+++ b/gsk/resources/vulkan/custom.vert
@@ -0,0 +1,53 @@
+#version 420 core
+
+layout(push_constant) uniform PushConstants {
+    mat4 mvp;
+    vec4 clip_bounds;
+    vec4 clip_widths;
+    vec4 clip_heights;
+} push;
+
+layout(location = 0) in vec4 inRect;
+layout(location = 1) in vec4 inTexRect1;
+layout(location = 2) in vec4 inTexRect2;
+layout(location = 3) in float inTime;
+
+layout(location = 0) out vec2 outPos;
+layout(location = 1) out vec2 outTexCoord1;
+layout(location = 2) out vec2 outTexCoord2;
+layout(location = 3) out float outTime;
+layout(location = 4) out vec2 outResolution;
+
+out gl_PerVertex {
+  vec4 gl_Position;
+};
+
+vec4 clip(vec4 rect) { return rect; }
+
+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);
+  vec4 texrect1 = vec4(inTexRect1.xy + inTexRect1.zw * texrect.xy,
+                      inTexRect1.zw * texrect.zw);
+  outTexCoord1 = texrect1.xy + texrect1.zw * offsets[gl_VertexIndex];
+
+  vec4 texrect2 = vec4(inTexRect2.xy + inTexRect2.zw * texrect.xy,
+                      inTexRect2.zw * texrect.zw);
+  outTexCoord2 = texrect2.xy + texrect2.zw * offsets[gl_VertexIndex];
+
+  outTime = inTime;
+  outResolution = inRect.zw;
+}
\ No newline at end of file
diff --git a/gsk/resources/vulkan/custom.vert.spv b/gsk/resources/vulkan/custom.vert.spv
new file mode 100644
index 0000000..c7f626b
Binary files /dev/null and b/gsk/resources/vulkan/custom.vert.spv differ
diff --git a/gsk/resources/vulkan/meson.build b/gsk/resources/vulkan/meson.build
index 3486a88..760e89a 100644
--- a/gsk/resources/vulkan/meson.build
+++ b/gsk/resources/vulkan/meson.build
@@ -32,6 +32,7 @@ gsk_private_vulkan_vertex_shaders = [
   'mask.vert',
   'outset-shadow.vert',
   'texture.vert',
+  'custom.vert'
 ]
 
 gsk_private_vulkan_shaders += gsk_private_vulkan_fragment_shaders



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