diff --git a/gsk/gskenums.h b/gsk/gskenums.h index ca9d5a8e56..b4d20beb7d 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 8c67fd8aa7..42112c839e 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 d560d8a554..e5e1e1113e 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 e7518e4ea0..857fae63e9 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 0000000000..b60b5f9d6b --- /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 0000000000..dc2f0b2066 --- /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 + +#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 38d5a5795f..341c683481 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); } @@ -73,6 +75,34 @@ gsk_vulkan_pipeline_new_full (GType pipeline_type, VkRenderPass render_pass, 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; @@ -80,7 +110,8 @@ gsk_vulkan_pipeline_new_full (GType pipeline_type, 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 cd41f822b3..6d03b54ecc 100644 --- a/gsk/gskvulkanpipelineprivate.h +++ b/gsk/gskvulkanpipelineprivate.h @@ -4,6 +4,7 @@ #include #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 ad4d1642bd..267ac7f973 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 770ebdb5e4..6bfa000cbf 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 1455bb7238..10e65401b5 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 1010b21304..7b9d2be87a 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 e94424f30a..11841afd89 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 a86e1566b6..e7c9e0ba13 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 0000000000..c7f626b30f 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 0000000000..c7f626b30f 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 0000000000..c36bb47165 --- /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 0000000000..c7f626b30f 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 3486a8868b..760e89a2e0 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