[gtk+/wip/matthiasc/shadertoy: 1/5] Support custom shaders in vulkan
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/matthiasc/shadertoy: 1/5] Support custom shaders in vulkan
- Date: Tue, 3 Oct 2017 22:09:42 +0000 (UTC)
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]