summaryrefslogtreecommitdiff
path: root/gsk/vulkan
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2018-01-06 09:36:55 -0500
committerMatthias Clasen <mclasen@redhat.com>2018-01-06 09:36:55 -0500
commit3771c95c72d591d3ed6ecf143fa85e1fea0a3d11 (patch)
treefb66e92aee87e28bee3c363a84f7874f763b7bda /gsk/vulkan
parent3c38ebb906319cb75688f9929b65ecf2a4fd3440 (diff)
downloadgtk+-3771c95c72d591d3ed6ecf143fa85e1fea0a3d11.tar.gz
gsk: Move Vulkan sources to a subdirectory
Following what was already done for GL.
Diffstat (limited to 'gsk/vulkan')
-rw-r--r--gsk/vulkan/gskvulkanblendmodepipeline.c146
-rw-r--r--gsk/vulkan/gskvulkanblendmodepipelineprivate.h37
-rw-r--r--gsk/vulkan/gskvulkanblurpipeline.c131
-rw-r--r--gsk/vulkan/gskvulkanblurpipelineprivate.h34
-rw-r--r--gsk/vulkan/gskvulkanborderpipeline.c164
-rw-r--r--gsk/vulkan/gskvulkanborderpipelineprivate.h35
-rw-r--r--gsk/vulkan/gskvulkanboxshadowpipeline.c162
-rw-r--r--gsk/vulkan/gskvulkanboxshadowpipelineprivate.h39
-rw-r--r--gsk/vulkan/gskvulkanbuffer.c110
-rw-r--r--gsk/vulkan/gskvulkanbufferprivate.h25
-rw-r--r--gsk/vulkan/gskvulkanclip.c187
-rw-r--r--gsk/vulkan/gskvulkanclipprivate.h57
-rw-r--r--gsk/vulkan/gskvulkancolorpipeline.c122
-rw-r--r--gsk/vulkan/gskvulkancolorpipelineprivate.h33
-rw-r--r--gsk/vulkan/gskvulkancolortextpipeline.c157
-rw-r--r--gsk/vulkan/gskvulkancolortextpipelineprivate.h43
-rw-r--r--gsk/vulkan/gskvulkancommandpool.c111
-rw-r--r--gsk/vulkan/gskvulkancommandpoolprivate.h26
-rw-r--r--gsk/vulkan/gskvulkancrossfadepipeline.c146
-rw-r--r--gsk/vulkan/gskvulkancrossfadepipelineprivate.h35
-rw-r--r--gsk/vulkan/gskvulkaneffectpipeline.c158
-rw-r--r--gsk/vulkan/gskvulkaneffectpipelineprivate.h35
-rw-r--r--gsk/vulkan/gskvulkanglyphcache.c483
-rw-r--r--gsk/vulkan/gskvulkanglyphcacheprivate.h28
-rw-r--r--gsk/vulkan/gskvulkanimage.c820
-rw-r--r--gsk/vulkan/gskvulkanimageprivate.h67
-rw-r--r--gsk/vulkan/gskvulkanlineargradientpipeline.c225
-rw-r--r--gsk/vulkan/gskvulkanlineargradientpipelineprivate.h42
-rw-r--r--gsk/vulkan/gskvulkanmemory.c95
-rw-r--r--gsk/vulkan/gskvulkanmemoryprivate.h23
-rw-r--r--gsk/vulkan/gskvulkanpipeline.c189
-rw-r--r--gsk/vulkan/gskvulkanpipelineprivate.h53
-rw-r--r--gsk/vulkan/gskvulkanpushconstants.c114
-rw-r--r--gsk/vulkan/gskvulkanpushconstantsprivate.h45
-rw-r--r--gsk/vulkan/gskvulkanrender.c761
-rw-r--r--gsk/vulkan/gskvulkanrenderer.c375
-rw-r--r--gsk/vulkan/gskvulkanrendererprivate.h62
-rw-r--r--gsk/vulkan/gskvulkanrenderpass.c1942
-rw-r--r--gsk/vulkan/gskvulkanrenderpassprivate.h45
-rw-r--r--gsk/vulkan/gskvulkanrenderprivate.h94
-rw-r--r--gsk/vulkan/gskvulkanshader.c102
-rw-r--r--gsk/vulkan/gskvulkanshaderprivate.h34
-rw-r--r--gsk/vulkan/gskvulkantextpipeline.c170
-rw-r--r--gsk/vulkan/gskvulkantextpipelineprivate.h44
-rw-r--r--gsk/vulkan/gskvulkantexturepipeline.c122
-rw-r--r--gsk/vulkan/gskvulkantexturepipelineprivate.h33
46 files changed, 7961 insertions, 0 deletions
diff --git a/gsk/vulkan/gskvulkanblendmodepipeline.c b/gsk/vulkan/gskvulkanblendmodepipeline.c
new file mode 100644
index 0000000000..117ed414d5
--- /dev/null
+++ b/gsk/vulkan/gskvulkanblendmodepipeline.c
@@ -0,0 +1,146 @@
+#include "config.h"
+
+#include "gskvulkanblendmodepipelineprivate.h"
+
+struct _GskVulkanBlendModePipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanBlendModeInstance GskVulkanBlendModeInstance;
+
+struct _GskVulkanBlendModeInstance
+{
+ float rect[4];
+ float start_tex_rect[4];
+ float end_tex_rect[4];
+ guint32 blend_mode;
+};
+
+G_DEFINE_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_blend_mode_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanBlendModeInstance),
+ .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 (GskVulkanBlendModeInstance, start_tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, end_tex_rect),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32_UINT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, blend_mode),
+ }
+ };
+ 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_blend_mode_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanBlendModePipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_blend_mode_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_blend_mode_pipeline_class_init (GskVulkanBlendModePipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_blend_mode_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_blend_mode_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_blend_mode_pipeline_init (GskVulkanBlendModePipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_blend_mode_pipeline_count_vertex_data (GskVulkanBlendModePipeline *pipeline)
+{
+ return sizeof (GskVulkanBlendModeInstance);
+}
+
+void
+gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *bounds,
+ const graphene_rect_t *start_tex_rect,
+ const graphene_rect_t *end_tex_rect,
+ GskBlendMode blend_mode)
+{
+ GskVulkanBlendModeInstance *instance = (GskVulkanBlendModeInstance *) 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->start_tex_rect[0] = start_tex_rect->origin.x;
+ instance->start_tex_rect[1] = start_tex_rect->origin.y;
+ instance->start_tex_rect[2] = start_tex_rect->size.width;
+ instance->start_tex_rect[3] = start_tex_rect->size.height;
+
+ instance->end_tex_rect[0] = end_tex_rect->origin.x;
+ instance->end_tex_rect[1] = end_tex_rect->origin.y;
+ instance->end_tex_rect[2] = end_tex_rect->size.width;
+ instance->end_tex_rect[3] = end_tex_rect->size.height;
+
+ instance->blend_mode = blend_mode;
+}
+
+gsize
+gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkanblendmodepipelineprivate.h b/gsk/vulkan/gskvulkanblendmodepipelineprivate.h
new file mode 100644
index 0000000000..67822e4f9b
--- /dev/null
+++ b/gsk/vulkan/gskvulkanblendmodepipelineprivate.h
@@ -0,0 +1,37 @@
+#ifndef __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskenums.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanBlendModePipelineLayout GskVulkanBlendModePipelineLayout;
+
+#define GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE (gsk_vulkan_blend_mode_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK, VULKAN_BLEND_MODE_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_blend_mode_pipeline_count_vertex_data (GskVulkanBlendModePipeline *pipeline);
+void gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *bounds,
+ const graphene_rect_t *start_bounds,
+ const graphene_rect_t *end_bounds,
+ GskBlendMode blend_mode);
+gsize gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanblurpipeline.c b/gsk/vulkan/gskvulkanblurpipeline.c
new file mode 100644
index 0000000000..5686e5423a
--- /dev/null
+++ b/gsk/vulkan/gskvulkanblurpipeline.c
@@ -0,0 +1,131 @@
+#include "config.h"
+
+#include "gskvulkanblurpipelineprivate.h"
+
+struct _GskVulkanBlurPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanBlurInstance GskVulkanBlurInstance;
+
+struct _GskVulkanBlurInstance
+{
+ float rect[4];
+ float tex_rect[4];
+ float blur_radius;
+};
+
+G_DEFINE_TYPE (GskVulkanBlurPipeline, gsk_vulkan_blur_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_blur_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanBlurInstance),
+ .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 (GskVulkanBlurInstance, tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBlurInstance, blur_radius),
+ }
+ };
+ 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_blur_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanBlurPipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_blur_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_blur_pipeline_class_init (GskVulkanBlurPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_blur_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_blur_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_blur_pipeline_init (GskVulkanBlurPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_blur_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BLUR_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_blur_pipeline_count_vertex_data (GskVulkanBlurPipeline *pipeline)
+{
+ return sizeof (GskVulkanBlurInstance);
+}
+
+void
+gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect,
+ double blur_radius)
+{
+ GskVulkanBlurInstance *instance = (GskVulkanBlurInstance *) data;
+
+ instance->rect[0] = rect->origin.x;
+ instance->rect[1] = rect->origin.y;
+ instance->rect[2] = rect->size.width;
+ instance->rect[3] = rect->size.height;
+ instance->tex_rect[0] = tex_rect->origin.x;
+ instance->tex_rect[1] = tex_rect->origin.y;
+ instance->tex_rect[2] = tex_rect->size.width;
+ instance->tex_rect[3] = tex_rect->size.height;
+ instance->blur_radius = blur_radius;
+}
+
+gsize
+gsk_vulkan_blur_pipeline_draw (GskVulkanBlurPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkanblurpipelineprivate.h b/gsk/vulkan/gskvulkanblurpipelineprivate.h
new file mode 100644
index 0000000000..0fc2cb6705
--- /dev/null
+++ b/gsk/vulkan/gskvulkanblurpipelineprivate.h
@@ -0,0 +1,34 @@
+#ifndef __GSK_VULKAN_BLUR_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_BLUR_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanBlurPipelineLayout GskVulkanBlurPipelineLayout;
+
+#define GSK_TYPE_VULKAN_BLUR_PIPELINE (gsk_vulkan_blur_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanBlurPipeline, gsk_vulkan_blur_pipeline, GSK, VULKAN_BLUR_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_blur_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_blur_pipeline_count_vertex_data (GskVulkanBlurPipeline *pipeline);
+void gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect,
+ double radius);
+gsize gsk_vulkan_blur_pipeline_draw (GskVulkanBlurPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_BLUR_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanborderpipeline.c b/gsk/vulkan/gskvulkanborderpipeline.c
new file mode 100644
index 0000000000..a0da43962b
--- /dev/null
+++ b/gsk/vulkan/gskvulkanborderpipeline.c
@@ -0,0 +1,164 @@
+#include "config.h"
+
+#include "gskvulkanborderpipelineprivate.h"
+
+#include "gskroundedrectprivate.h"
+
+struct _GskVulkanBorderPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanBorderInstance GskVulkanBorderInstance;
+
+struct _GskVulkanBorderInstance
+{
+ float rect[12];
+ float widths[4];
+ float colors[16];
+};
+
+G_DEFINE_TYPE (GskVulkanBorderPipeline, gsk_vulkan_border_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_border_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanBorderInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect) + 4 * sizeof (float),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, rect) + 8 * sizeof (float),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, widths),
+ },
+ {
+ .location = 4,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors),
+ },
+ {
+ .location = 5,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 4 * sizeof (float),
+ },
+ {
+ .location = 6,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 8 * sizeof (float),
+ },
+ {
+ .location = 7,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBorderInstance, colors) + 12 * sizeof (float),
+ }
+ };
+ 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_border_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanBorderPipeline *self = GSK_VULKAN_BORDER_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_border_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_border_pipeline_class_init (GskVulkanBorderPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_border_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_border_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_border_pipeline_init (GskVulkanBorderPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_border_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BORDER_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_border_pipeline_count_vertex_data (GskVulkanBorderPipeline *pipeline)
+{
+ return sizeof (GskVulkanBorderInstance);
+}
+
+void
+gsk_vulkan_border_pipeline_collect_vertex_data (GskVulkanBorderPipeline *pipeline,
+ guchar *data,
+ const GskRoundedRect *rect,
+ const float widths[4],
+ const GdkRGBA colors[4])
+{
+ GskVulkanBorderInstance *instance = (GskVulkanBorderInstance *) data;
+ guint i;
+
+ gsk_rounded_rect_to_float (rect, instance->rect);
+ for (i = 0; i < 4; i++)
+ {
+ instance->widths[i] = widths[i];
+ instance->colors[4 * i + 0] = colors[i].red;
+ instance->colors[4 * i + 1] = colors[i].green;
+ instance->colors[4 * i + 2] = colors[i].blue;
+ instance->colors[4 * i + 3] = colors[i].alpha;
+ }
+}
+
+gsize
+gsk_vulkan_border_pipeline_draw (GskVulkanBorderPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6 * 8, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkanborderpipelineprivate.h b/gsk/vulkan/gskvulkanborderpipelineprivate.h
new file mode 100644
index 0000000000..d82679259d
--- /dev/null
+++ b/gsk/vulkan/gskvulkanborderpipelineprivate.h
@@ -0,0 +1,35 @@
+#ifndef __GSK_VULKAN_BORDER_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_BORDER_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskroundedrect.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanBorderPipelineLayout GskVulkanBorderPipelineLayout;
+
+#define GSK_TYPE_VULKAN_BORDER_PIPELINE (gsk_vulkan_border_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanBorderPipeline, gsk_vulkan_border_pipeline, GSK, VULKAN_BORDER_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_border_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_border_pipeline_count_vertex_data (GskVulkanBorderPipeline *pipeline);
+void gsk_vulkan_border_pipeline_collect_vertex_data (GskVulkanBorderPipeline *pipeline,
+ guchar *data,
+ const GskRoundedRect *rect,
+ const float widths[4],
+ const GdkRGBA colors[4]);
+gsize gsk_vulkan_border_pipeline_draw (GskVulkanBorderPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_BORDER_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanboxshadowpipeline.c b/gsk/vulkan/gskvulkanboxshadowpipeline.c
new file mode 100644
index 0000000000..fb272176d4
--- /dev/null
+++ b/gsk/vulkan/gskvulkanboxshadowpipeline.c
@@ -0,0 +1,162 @@
+#include "config.h"
+
+#include "gskvulkanboxshadowpipelineprivate.h"
+
+#include "gskroundedrectprivate.h"
+
+struct _GskVulkanBoxShadowPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanBoxShadowInstance GskVulkanBoxShadowInstance;
+
+struct _GskVulkanBoxShadowInstance
+{
+ float outline[12];
+ float color[4];
+ float offset[2];
+ float spread;
+ float blur_radius;
+};
+
+G_DEFINE_TYPE (GskVulkanBoxShadowPipeline, gsk_vulkan_box_shadow_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_box_shadow_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanBoxShadowInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline) + 4 * sizeof (float),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, outline) + 8 * sizeof (float),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, color),
+ },
+ {
+ .location = 4,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, offset),
+ },
+ {
+ .location = 5,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, spread),
+ },
+ {
+ .location = 6,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanBoxShadowInstance, blur_radius),
+ }
+ };
+ 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_box_shadow_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanBoxShadowPipeline *self = GSK_VULKAN_BOX_SHADOW_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_box_shadow_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_box_shadow_pipeline_class_init (GskVulkanBoxShadowPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_box_shadow_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_box_shadow_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_box_shadow_pipeline_init (GskVulkanBoxShadowPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_box_shadow_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BOX_SHADOW_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_box_shadow_pipeline_count_vertex_data (GskVulkanBoxShadowPipeline *pipeline)
+{
+ return sizeof (GskVulkanBoxShadowInstance);
+}
+
+void
+gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GskVulkanBoxShadowPipeline *pipeline,
+ guchar *data,
+ const GskRoundedRect *outline,
+ const GdkRGBA *color,
+ float dx,
+ float dy,
+ float spread,
+ float blur_radius)
+{
+ GskVulkanBoxShadowInstance *instance = (GskVulkanBoxShadowInstance *) data;
+
+ gsk_rounded_rect_to_float (outline, instance->outline);
+ instance->color[0] = color->red;
+ instance->color[1] = color->green;
+ instance->color[2] = color->blue;
+ instance->color[3] = color->alpha;
+ instance->offset[0] = dx;
+ instance->offset[1] = dy;
+ instance->spread = spread;
+ instance->blur_radius = blur_radius;
+}
+
+gsize
+gsk_vulkan_box_shadow_pipeline_draw (GskVulkanBoxShadowPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6 * 8, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkanboxshadowpipelineprivate.h b/gsk/vulkan/gskvulkanboxshadowpipelineprivate.h
new file mode 100644
index 0000000000..975338bcf1
--- /dev/null
+++ b/gsk/vulkan/gskvulkanboxshadowpipelineprivate.h
@@ -0,0 +1,39 @@
+#ifndef __GSK_VULKAN_BOX_SHADOW_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_BOX_SHADOW_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskroundedrect.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanBoxShadowPipelineLayout GskVulkanBoxShadowPipelineLayout;
+
+#define GSK_TYPE_VULKAN_BOX_SHADOW_PIPELINE (gsk_vulkan_box_shadow_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanBoxShadowPipeline, gsk_vulkan_box_shadow_pipeline, GSK, VULKAN_BOX_SHADOW_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_box_shadow_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_box_shadow_pipeline_count_vertex_data (GskVulkanBoxShadowPipeline *pipeline);
+void gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GskVulkanBoxShadowPipeline *pipeline,
+ guchar *data,
+ const GskRoundedRect *outline,
+ const GdkRGBA *color,
+ float dx,
+ float dy,
+ float spread,
+ float blur_radius);
+
+gsize gsk_vulkan_box_shadow_pipeline_draw (GskVulkanBoxShadowPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_BOX_SHADOW_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanbuffer.c b/gsk/vulkan/gskvulkanbuffer.c
new file mode 100644
index 0000000000..291e340170
--- /dev/null
+++ b/gsk/vulkan/gskvulkanbuffer.c
@@ -0,0 +1,110 @@
+#include "config.h"
+
+#include "gskvulkanbufferprivate.h"
+#include "gskvulkanmemoryprivate.h"
+#include "gskvulkanpipelineprivate.h"
+
+struct _GskVulkanBuffer
+{
+ GdkVulkanContext *vulkan;
+
+ gsize size;
+
+ VkBuffer vk_buffer;
+
+ GskVulkanMemory *memory;
+};
+
+static GskVulkanBuffer *
+gsk_vulkan_buffer_new_internal (GdkVulkanContext *context,
+ gsize size,
+ VkBufferUsageFlags usage)
+{
+ VkMemoryRequirements requirements;
+ GskVulkanBuffer *self;
+
+ self = g_slice_new0 (GskVulkanBuffer);
+
+ self->vulkan = g_object_ref (context);
+ self->size = size;
+
+ GSK_VK_CHECK (vkCreateBuffer, gdk_vulkan_context_get_device (context),
+ &(VkBufferCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = size,
+ .flags = 0,
+ .usage = usage,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE
+ },
+ NULL,
+ &self->vk_buffer);
+
+ vkGetBufferMemoryRequirements (gdk_vulkan_context_get_device (context),
+ self->vk_buffer,
+ &requirements);
+
+ self->memory = gsk_vulkan_memory_new (context,
+ requirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ size);
+
+ GSK_VK_CHECK (vkBindBufferMemory, gdk_vulkan_context_get_device (context),
+ self->vk_buffer,
+ gsk_vulkan_memory_get_device_memory (self->memory),
+ 0);
+ return self;
+}
+
+GskVulkanBuffer *
+gsk_vulkan_buffer_new (GdkVulkanContext *context,
+ gsize size)
+{
+ return gsk_vulkan_buffer_new_internal (context, size,
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
+ | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
+}
+
+GskVulkanBuffer *
+gsk_vulkan_buffer_new_staging (GdkVulkanContext *context,
+ gsize size)
+{
+ return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
+}
+
+GskVulkanBuffer *
+gsk_vulkan_buffer_new_download (GdkVulkanContext *context,
+ gsize size)
+{
+ return gsk_vulkan_buffer_new_internal (context, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+}
+void
+gsk_vulkan_buffer_free (GskVulkanBuffer *self)
+{
+ vkDestroyBuffer (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_buffer,
+ NULL);
+
+ gsk_vulkan_memory_free (self->memory);
+
+ g_object_unref (self->vulkan);
+
+ g_slice_free (GskVulkanBuffer, self);
+}
+
+VkBuffer
+gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self)
+{
+ return self->vk_buffer;
+}
+
+guchar *
+gsk_vulkan_buffer_map (GskVulkanBuffer *self)
+{
+ return gsk_vulkan_memory_map (self->memory);
+}
+
+void
+gsk_vulkan_buffer_unmap (GskVulkanBuffer *self)
+{
+ gsk_vulkan_memory_unmap (self->memory);
+}
diff --git a/gsk/vulkan/gskvulkanbufferprivate.h b/gsk/vulkan/gskvulkanbufferprivate.h
new file mode 100644
index 0000000000..700e400528
--- /dev/null
+++ b/gsk/vulkan/gskvulkanbufferprivate.h
@@ -0,0 +1,25 @@
+#ifndef __GSK_VULKAN_BUFFER_PRIVATE_H__
+#define __GSK_VULKAN_BUFFER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanBuffer GskVulkanBuffer;
+
+GskVulkanBuffer * gsk_vulkan_buffer_new (GdkVulkanContext *context,
+ gsize size);
+GskVulkanBuffer * gsk_vulkan_buffer_new_staging (GdkVulkanContext *context,
+ gsize size);
+GskVulkanBuffer * gsk_vulkan_buffer_new_download (GdkVulkanContext *context,
+ gsize size);
+void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer);
+
+VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self);
+
+guchar * gsk_vulkan_buffer_map (GskVulkanBuffer *self);
+void gsk_vulkan_buffer_unmap (GskVulkanBuffer *self);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_BUFFER_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanclip.c b/gsk/vulkan/gskvulkanclip.c
new file mode 100644
index 0000000000..d927d88f13
--- /dev/null
+++ b/gsk/vulkan/gskvulkanclip.c
@@ -0,0 +1,187 @@
+#include "config.h"
+
+#include "gskvulkanclipprivate.h"
+
+#include "gskroundedrectprivate.h"
+
+void
+gsk_vulkan_clip_init_empty (GskVulkanClip *clip,
+ const graphene_rect_t *rect)
+{
+ clip->type = GSK_VULKAN_CLIP_NONE;
+ gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0);
+}
+
+static void
+gsk_vulkan_clip_init_copy (GskVulkanClip *self,
+ const GskVulkanClip *src)
+{
+ self->type = src->type;
+ gsk_rounded_rect_init_copy (&self->rect, &src->rect);
+}
+
+gboolean
+gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_rect_t *rect)
+{
+ if (graphene_rect_contains_rect (rect, &src->rect.bounds))
+ {
+ gsk_vulkan_clip_init_copy (dest, src);
+ return TRUE;
+ }
+ if (!graphene_rect_intersection (rect, &src->rect.bounds, NULL))
+ {
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ return TRUE;
+ }
+
+ switch (src->type)
+ {
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_NONE:
+ gsk_vulkan_clip_init_copy (dest, src);
+ if (graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds))
+ dest->type = GSK_VULKAN_CLIP_RECT;
+ else
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_RECT:
+ gsk_vulkan_clip_init_copy (dest, src);
+ if (!graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds))
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ if (gsk_rounded_rect_contains_rect (&src->rect, rect))
+ {
+ dest->type = GSK_VULKAN_CLIP_RECT;
+ gsk_rounded_rect_init_from_rect (&dest->rect, rect, 0);
+ }
+ else
+ {
+ /* some points of rect are inside src's rounded rect,
+ * some are outside. */
+ /* XXX: If the 2 rects don't intersect on rounded corners,
+ * we could actually compute a new clip here.
+ */
+ return FALSE;
+ }
+
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const GskRoundedRect *rounded)
+{
+ if (gsk_rounded_rect_contains_rect (rounded, &src->rect.bounds))
+ {
+ gsk_vulkan_clip_init_copy (dest, src);
+ return TRUE;
+ }
+ if (!graphene_rect_intersection (&rounded->bounds, &src->rect.bounds, NULL))
+ {
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ return TRUE;
+ }
+
+ switch (src->type)
+ {
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_NONE:
+ dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED;
+ gsk_rounded_rect_init_copy (&dest->rect, rounded);
+ break;
+
+ case GSK_VULKAN_CLIP_RECT:
+ if (graphene_rect_contains_rect (&src->rect.bounds, &rounded->bounds))
+ {
+ dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED;
+ gsk_rounded_rect_init_copy (&dest->rect, rounded);
+ return TRUE;
+ }
+ /* some points of rect are inside src's rounded rect,
+ * some are outside. */
+ /* XXX: If the 2 rects don't intersect on rounded corners,
+ * we could actually compute a new clip here.
+ */
+ return FALSE;
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ /* XXX: improve */
+ return FALSE;
+
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_clip_transform (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_matrix_t *transform,
+ const graphene_rect_t *viewport)
+{
+ switch (src->type)
+ {
+ default:
+ g_assert_not_reached();
+ return FALSE;
+
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ gsk_vulkan_clip_init_copy (dest, src);
+ return TRUE;
+
+ case GSK_VULKAN_CLIP_NONE:
+ gsk_vulkan_clip_init_empty (dest, viewport);
+ return TRUE;
+
+ case GSK_VULKAN_CLIP_RECT:
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ /* FIXME: Handle 2D operations, in particular transform and scale */
+ return FALSE;
+ }
+}
+
+gboolean
+gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
+ const graphene_rect_t *rect)
+{
+ switch (self->type)
+ {
+ default:
+ g_assert_not_reached();
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ return FALSE;
+
+ case GSK_VULKAN_CLIP_NONE:
+ return TRUE;
+
+ case GSK_VULKAN_CLIP_RECT:
+ return graphene_rect_contains_rect (&self->rect.bounds, rect);
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ return gsk_rounded_rect_contains_rect (&self->rect, rect);
+ }
+}
diff --git a/gsk/vulkan/gskvulkanclipprivate.h b/gsk/vulkan/gskvulkanclipprivate.h
new file mode 100644
index 0000000000..10b89edcba
--- /dev/null
+++ b/gsk/vulkan/gskvulkanclipprivate.h
@@ -0,0 +1,57 @@
+#ifndef __GSK_VULKAN_CLIP_PRIVATE_H__
+#define __GSK_VULKAN_CLIP_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <graphene.h>
+#include <gsk/gskroundedrect.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ /* The whole area is clipped, no drawing is necessary.
+ * This can't be handled by return values because for return
+ * values we return if clips could even be computed.
+ */
+ GSK_VULKAN_CLIP_ALL_CLIPPED,
+ /* No clipping is necessary, but the clip rect is set
+ * to the actual bounds of the underlying framebuffer
+ */
+ GSK_VULKAN_CLIP_NONE,
+ /* The clip is a rectangular area */
+ GSK_VULKAN_CLIP_RECT,
+ /* The clip is a rounded rectangle, and for every corner
+ * corner.width == corner.height is true
+ */
+ GSK_VULKAN_CLIP_ROUNDED_CIRCULAR,
+ /* The clip is a rounded rectangle */
+ GSK_VULKAN_CLIP_ROUNDED
+} GskVulkanClipComplexity;
+
+typedef struct _GskVulkanClip GskVulkanClip;
+
+struct _GskVulkanClip
+{
+ GskVulkanClipComplexity type;
+ GskRoundedRect rect;
+};
+
+void gsk_vulkan_clip_init_empty (GskVulkanClip *clip,
+ const graphene_rect_t *rect);
+
+gboolean gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
+gboolean gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT;
+gboolean gsk_vulkan_clip_transform (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_matrix_t*transform,
+ const graphene_rect_t *viewport) G_GNUC_WARN_UNUSED_RESULT;
+
+gboolean gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
+ const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_CLIP_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkancolorpipeline.c b/gsk/vulkan/gskvulkancolorpipeline.c
new file mode 100644
index 0000000000..cd6867a1a4
--- /dev/null
+++ b/gsk/vulkan/gskvulkancolorpipeline.c
@@ -0,0 +1,122 @@
+#include "config.h"
+
+#include "gskvulkancolorpipelineprivate.h"
+
+struct _GskVulkanColorPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanColorInstance GskVulkanColorInstance;
+
+struct _GskVulkanColorInstance
+{
+ float rect[4];
+ float color[4];
+};
+
+G_DEFINE_TYPE (GskVulkanColorPipeline, gsk_vulkan_color_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_color_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanColorInstance),
+ .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 (GskVulkanColorInstance, color),
+ }
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_color_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanColorPipeline *self = GSK_VULKAN_COLOR_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_color_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_color_pipeline_class_init (GskVulkanColorPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_color_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_color_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_color_pipeline_init (GskVulkanColorPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_color_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_COLOR_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_color_pipeline_count_vertex_data (GskVulkanColorPipeline *pipeline)
+{
+ return sizeof (GskVulkanColorInstance);
+}
+
+void
+gsk_vulkan_color_pipeline_collect_vertex_data (GskVulkanColorPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const GdkRGBA *color)
+{
+ GskVulkanColorInstance *instance = (GskVulkanColorInstance *) data;
+
+ instance->rect[0] = rect->origin.x;
+ instance->rect[1] = rect->origin.y;
+ instance->rect[2] = rect->size.width;
+ instance->rect[3] = rect->size.height;
+ instance->color[0] = color->red;
+ instance->color[1] = color->green;
+ instance->color[2] = color->blue;
+ instance->color[3] = color->alpha;
+}
+
+gsize
+gsk_vulkan_color_pipeline_draw (GskVulkanColorPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkancolorpipelineprivate.h b/gsk/vulkan/gskvulkancolorpipelineprivate.h
new file mode 100644
index 0000000000..b9ae514d29
--- /dev/null
+++ b/gsk/vulkan/gskvulkancolorpipelineprivate.h
@@ -0,0 +1,33 @@
+#ifndef __GSK_VULKAN_COLOR_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_COLOR_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanColorPipelineLayout GskVulkanColorPipelineLayout;
+
+#define GSK_TYPE_VULKAN_COLOR_PIPELINE (gsk_vulkan_color_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanColorPipeline, gsk_vulkan_color_pipeline, GSK, VULKAN_COLOR_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_color_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_color_pipeline_count_vertex_data (GskVulkanColorPipeline *pipeline);
+void gsk_vulkan_color_pipeline_collect_vertex_data (GskVulkanColorPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const GdkRGBA *color);
+gsize gsk_vulkan_color_pipeline_draw (GskVulkanColorPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_COLOR_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkancolortextpipeline.c b/gsk/vulkan/gskvulkancolortextpipeline.c
new file mode 100644
index 0000000000..9770b27938
--- /dev/null
+++ b/gsk/vulkan/gskvulkancolortextpipeline.c
@@ -0,0 +1,157 @@
+#include "config.h"
+
+#include "gskvulkancolortextpipelineprivate.h"
+
+struct _GskVulkanColorTextPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanColorTextInstance GskVulkanColorTextInstance;
+
+struct _GskVulkanColorTextInstance
+{
+ float rect[4];
+ float tex_rect[4];
+};
+
+G_DEFINE_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_color_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanColorTextInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanColorTextInstance, tex_rect),
+ },
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_color_text_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanColorTextPipeline *self = GSK_VULKAN_COLOR_TEXT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_color_text_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_color_text_pipeline_class_init (GskVulkanColorTextPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_color_text_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_color_text_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_color_text_pipeline_init (GskVulkanColorTextPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_color_text_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new_full (GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE, context, layout, shader_name, render_pass,
+ VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+gsize
+gsk_vulkan_color_text_pipeline_count_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ int num_instances)
+{
+ return sizeof (GskVulkanColorTextInstance) * num_instances;
+}
+
+void
+gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ guint total_glyphs,
+ const PangoGlyphInfo *glyphs,
+ float x,
+ float y,
+ guint start_glyph,
+ guint num_glyphs,
+ float scale)
+{
+ GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
+ int i;
+ int count = 0;
+ int x_position = 0;
+
+ for (i = 0; i < start_glyph; i++)
+ x_position += glyphs[i].geometry.width;
+
+ for (; i < total_glyphs && count < num_glyphs; i++)
+ {
+ const PangoGlyphInfo *gi = &glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
+ GskVulkanColorTextInstance *instance = &instances[count];
+ GskVulkanCachedGlyph *glyph;
+
+ glyph = gsk_vulkan_renderer_get_cached_glyph (renderer, font, gi->glyph, scale);
+
+ instance->tex_rect[0] = glyph->tx;
+ instance->tex_rect[1] = glyph->ty;
+ instance->tex_rect[2] = glyph->tw;
+ instance->tex_rect[3] = glyph->th;
+
+ instance->rect[0] = x + cx + glyph->draw_x;
+ instance->rect[1] = y + cy + glyph->draw_y;
+ instance->rect[2] = glyph->draw_width;
+ instance->rect[3] = glyph->draw_height;
+
+ count++;
+ }
+ x_position += gi->geometry.width;
+ }
+}
+
+gsize
+gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkancolortextpipelineprivate.h b/gsk/vulkan/gskvulkancolortextpipelineprivate.h
new file mode 100644
index 0000000000..2e46b1c6a8
--- /dev/null
+++ b/gsk/vulkan/gskvulkancolortextpipelineprivate.h
@@ -0,0 +1,43 @@
+#ifndef __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrendererprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanColorTextPipelineLayout GskVulkanColorTextPipelineLayout;
+
+#define GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE (gsk_vulkan_color_text_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK, VULKAN_COLOR_TEXT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_color_text_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_color_text_pipeline_count_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ int num_instances);
+void gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ guint total_glyphs,
+ const PangoGlyphInfo *glyphs,
+ float x,
+ float y,
+ guint start_glyph,
+ guint num_glyphs,
+ float scale);
+gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_COLOR_TEXT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkancommandpool.c b/gsk/vulkan/gskvulkancommandpool.c
new file mode 100644
index 0000000000..bcba6a9fc1
--- /dev/null
+++ b/gsk/vulkan/gskvulkancommandpool.c
@@ -0,0 +1,111 @@
+#include "config.h"
+
+#include "gskvulkancommandpoolprivate.h"
+#include "gskvulkanpipelineprivate.h"
+
+struct _GskVulkanCommandPool
+{
+ GdkVulkanContext *vulkan;
+
+ VkCommandPool vk_command_pool;
+};
+
+GskVulkanCommandPool *
+gsk_vulkan_command_pool_new (GdkVulkanContext *context)
+{
+ GskVulkanCommandPool *self;
+
+ self = g_slice_new0 (GskVulkanCommandPool);
+
+ self->vulkan = g_object_ref (context);
+
+ GSK_VK_CHECK (vkCreateCommandPool, gdk_vulkan_context_get_device (context),
+ &(const VkCommandPoolCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .queueFamilyIndex = gdk_vulkan_context_get_queue_family_index (self->vulkan),
+ .flags = 0
+ },
+ NULL,
+ &self->vk_command_pool);
+
+ return self;
+}
+
+void
+gsk_vulkan_command_pool_free (GskVulkanCommandPool *self)
+{
+ vkDestroyCommandPool (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_command_pool,
+ NULL);
+
+ g_slice_free (GskVulkanCommandPool, self);
+}
+
+void
+gsk_vulkan_command_pool_reset (GskVulkanCommandPool *self)
+{
+ GSK_VK_CHECK (vkResetCommandPool, gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_command_pool,
+ 0);
+}
+
+VkCommandBuffer
+gsk_vulkan_command_pool_get_buffer (GskVulkanCommandPool *self)
+{
+ VkCommandBuffer command_buffer;
+
+ GSK_VK_CHECK (vkAllocateCommandBuffers, gdk_vulkan_context_get_device (self->vulkan),
+ &(VkCommandBufferAllocateInfo) {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = self->vk_command_pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ },
+ &command_buffer);
+
+ GSK_VK_CHECK (vkBeginCommandBuffer, command_buffer,
+ &(VkCommandBufferBeginInfo) {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = 0
+ });
+
+ return command_buffer;
+}
+
+void
+gsk_vulkan_command_pool_submit_buffer (GskVulkanCommandPool *self,
+ VkCommandBuffer command_buffer,
+ gsize wait_semaphore_count,
+ VkSemaphore *wait_semaphores,
+ gsize signal_semaphore_count,
+ VkSemaphore *signal_semaphores,
+ VkFence fence)
+{
+ VkPipelineStageFlags *wait_semaphore_flags = NULL;
+
+ GSK_VK_CHECK (vkEndCommandBuffer, command_buffer);
+
+ if (wait_semaphore_count > 0)
+ {
+ wait_semaphore_flags = alloca (sizeof (VkPipelineStageFlags) * wait_semaphore_count);
+ for (int i = 0; i < wait_semaphore_count; i++)
+ wait_semaphore_flags[i] = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ }
+
+ GSK_VK_CHECK (vkQueueSubmit, gdk_vulkan_context_get_queue (self->vulkan),
+ 1,
+ &(VkSubmitInfo) {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .waitSemaphoreCount = wait_semaphore_count,
+ .pWaitSemaphores = wait_semaphores,
+ .pWaitDstStageMask = wait_semaphore_flags,
+ .commandBufferCount = 1,
+ .pCommandBuffers = (VkCommandBuffer[1]) {
+ command_buffer
+ },
+ .signalSemaphoreCount = signal_semaphore_count,
+ .pSignalSemaphores = signal_semaphores,
+ },
+ fence);
+}
+
diff --git a/gsk/vulkan/gskvulkancommandpoolprivate.h b/gsk/vulkan/gskvulkancommandpoolprivate.h
new file mode 100644
index 0000000000..bb362f3fd9
--- /dev/null
+++ b/gsk/vulkan/gskvulkancommandpoolprivate.h
@@ -0,0 +1,26 @@
+#ifndef __GSK_VULKAN_COMMAND_POOL_PRIVATE_H__
+#define __GSK_VULKAN_COMMAND_POOL_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanCommandPool GskVulkanCommandPool;
+
+GskVulkanCommandPool * gsk_vulkan_command_pool_new (GdkVulkanContext *context);
+void gsk_vulkan_command_pool_free (GskVulkanCommandPool *self);
+
+void gsk_vulkan_command_pool_reset (GskVulkanCommandPool *self);
+
+VkCommandBuffer gsk_vulkan_command_pool_get_buffer (GskVulkanCommandPool *self);
+void gsk_vulkan_command_pool_submit_buffer (GskVulkanCommandPool *self,
+ VkCommandBuffer buffer,
+ gsize wait_semaphore_count,
+ VkSemaphore *wait_semaphores,
+ gsize signal_semaphores_count,
+ VkSemaphore *signal_semaphores,
+ VkFence fence);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_COMMAND_POOL_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkancrossfadepipeline.c b/gsk/vulkan/gskvulkancrossfadepipeline.c
new file mode 100644
index 0000000000..679c583c6e
--- /dev/null
+++ b/gsk/vulkan/gskvulkancrossfadepipeline.c
@@ -0,0 +1,146 @@
+#include "config.h"
+
+#include "gskvulkancrossfadepipelineprivate.h"
+
+struct _GskVulkanCrossFadePipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanCrossFadeInstance GskVulkanCrossFadeInstance;
+
+struct _GskVulkanCrossFadeInstance
+{
+ float rect[4];
+ float start_tex_rect[4];
+ float end_tex_rect[4];
+ float progress;
+};
+
+G_DEFINE_TYPE (GskVulkanCrossFadePipeline, gsk_vulkan_cross_fade_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_cross_fade_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanCrossFadeInstance),
+ .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 (GskVulkanCrossFadeInstance, start_tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, end_tex_rect),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanCrossFadeInstance, progress),
+ }
+ };
+ 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_cross_fade_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanCrossFadePipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_cross_fade_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_cross_fade_pipeline_class_init (GskVulkanCrossFadePipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_cross_fade_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_cross_fade_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_cross_fade_pipeline_init (GskVulkanCrossFadePipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_cross_fade_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_CROSS_FADE_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_cross_fade_pipeline_count_vertex_data (GskVulkanCrossFadePipeline *pipeline)
+{
+ return sizeof (GskVulkanCrossFadeInstance);
+}
+
+void
+gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GskVulkanCrossFadePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *bounds,
+ const graphene_rect_t *start_tex_rect,
+ const graphene_rect_t *end_tex_rect,
+ double progress)
+{
+ GskVulkanCrossFadeInstance *instance = (GskVulkanCrossFadeInstance *) 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->start_tex_rect[0] = start_tex_rect->origin.x;
+ instance->start_tex_rect[1] = start_tex_rect->origin.y;
+ instance->start_tex_rect[2] = start_tex_rect->size.width;
+ instance->start_tex_rect[3] = start_tex_rect->size.height;
+
+ instance->end_tex_rect[0] = end_tex_rect->origin.x;
+ instance->end_tex_rect[1] = end_tex_rect->origin.y;
+ instance->end_tex_rect[2] = end_tex_rect->size.width;
+ instance->end_tex_rect[3] = end_tex_rect->size.height;
+
+ instance->progress = progress;
+}
+
+gsize
+gsk_vulkan_cross_fade_pipeline_draw (GskVulkanCrossFadePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkancrossfadepipelineprivate.h b/gsk/vulkan/gskvulkancrossfadepipelineprivate.h
new file mode 100644
index 0000000000..da5e820365
--- /dev/null
+++ b/gsk/vulkan/gskvulkancrossfadepipelineprivate.h
@@ -0,0 +1,35 @@
+#ifndef __GSK_VULKAN_CROSS_FADE_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_CROSS_FADE_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanCrossFadePipelineLayout GskVulkanCrossFadePipelineLayout;
+
+#define GSK_TYPE_VULKAN_CROSS_FADE_PIPELINE (gsk_vulkan_cross_fade_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanCrossFadePipeline, gsk_vulkan_cross_fade_pipeline, GSK, VULKAN_CROSS_FADE_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_cross_fade_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_cross_fade_pipeline_count_vertex_data (GskVulkanCrossFadePipeline *pipeline);
+void gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GskVulkanCrossFadePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *bounds,
+ const graphene_rect_t *start_bounds,
+ const graphene_rect_t *end_bounds,
+ double progress);
+gsize gsk_vulkan_cross_fade_pipeline_draw (GskVulkanCrossFadePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_CROSS_FADE_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkaneffectpipeline.c b/gsk/vulkan/gskvulkaneffectpipeline.c
new file mode 100644
index 0000000000..aa9973c711
--- /dev/null
+++ b/gsk/vulkan/gskvulkaneffectpipeline.c
@@ -0,0 +1,158 @@
+#include "config.h"
+
+#include "gskvulkaneffectpipelineprivate.h"
+
+struct _GskVulkanEffectPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanEffectInstance GskVulkanEffectInstance;
+
+struct _GskVulkanEffectInstance
+{
+ float rect[4];
+ float tex_rect[4];
+ float color_matrix[16];
+ float color_offset[4];
+};
+
+G_DEFINE_TYPE (GskVulkanEffectPipeline, gsk_vulkan_effect_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_effect_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanEffectInstance),
+ .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 (GskVulkanEffectInstance, tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 4,
+ },
+ {
+ .location = 4,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 8,
+ },
+ {
+ .location = 5,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_matrix) + sizeof (float) * 12,
+ },
+ {
+ .location = 6,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanEffectInstance, color_offset),
+ }
+ };
+ 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_effect_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanEffectPipeline *self = GSK_VULKAN_EFFECT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_effect_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_effect_pipeline_class_init (GskVulkanEffectPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_effect_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_effect_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_effect_pipeline_init (GskVulkanEffectPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_effect_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_EFFECT_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_effect_pipeline_count_vertex_data (GskVulkanEffectPipeline *pipeline)
+{
+ return sizeof (GskVulkanEffectInstance);
+}
+
+void
+gsk_vulkan_effect_pipeline_collect_vertex_data (GskVulkanEffectPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect,
+ const graphene_matrix_t *color_matrix,
+ const graphene_vec4_t *color_offset)
+{
+ GskVulkanEffectInstance *instance = (GskVulkanEffectInstance *) data;
+
+ instance->rect[0] = rect->origin.x;
+ instance->rect[1] = rect->origin.y;
+ instance->rect[2] = rect->size.width;
+ instance->rect[3] = rect->size.height;
+ instance->tex_rect[0] = tex_rect->origin.x;
+ instance->tex_rect[1] = tex_rect->origin.y;
+ instance->tex_rect[2] = tex_rect->size.width;
+ instance->tex_rect[3] = tex_rect->size.height;
+ graphene_matrix_to_float (color_matrix, instance->color_matrix);
+ graphene_vec4_to_float (color_offset, instance->color_offset);
+}
+
+gsize
+gsk_vulkan_effect_pipeline_draw (GskVulkanEffectPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkaneffectpipelineprivate.h b/gsk/vulkan/gskvulkaneffectpipelineprivate.h
new file mode 100644
index 0000000000..45ac6af9c8
--- /dev/null
+++ b/gsk/vulkan/gskvulkaneffectpipelineprivate.h
@@ -0,0 +1,35 @@
+#ifndef __GSK_VULKAN_EFFECT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_EFFECT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanEffectPipelineLayout GskVulkanEffectPipelineLayout;
+
+#define GSK_TYPE_VULKAN_EFFECT_PIPELINE (gsk_vulkan_effect_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanEffectPipeline, gsk_vulkan_effect_pipeline, GSK, VULKAN_EFFECT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_effect_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_effect_pipeline_count_vertex_data (GskVulkanEffectPipeline *pipeline);
+void gsk_vulkan_effect_pipeline_collect_vertex_data (GskVulkanEffectPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect,
+ const graphene_matrix_t *color_matrix,
+ const graphene_vec4_t *color_offset);
+gsize gsk_vulkan_effect_pipeline_draw (GskVulkanEffectPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_EFFECT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanglyphcache.c b/gsk/vulkan/gskvulkanglyphcache.c
new file mode 100644
index 0000000000..8d12f5ce66
--- /dev/null
+++ b/gsk/vulkan/gskvulkanglyphcache.c
@@ -0,0 +1,483 @@
+#include "config.h"
+
+#include "gskvulkanglyphcacheprivate.h"
+
+#include "gskvulkanimageprivate.h"
+#include "gskdebugprivate.h"
+#include "gskprivate.h"
+
+#include <graphene.h>
+
+/* Parameters for our cache eviction strategy.
+ *
+ * Each cached glyph has an age that gets reset every time a cached glyph gets used.
+ * Glyphs that have not been used for the MAX_AGE frames are considered old. We keep
+ * count of the pixels of each atlas that are taken up by old glyphs. We check the
+ * fraction of old pixels every CHECK_INTERVAL frames, and if it is above MAX_OLD, then
+ * we drop the atlas an all the glyphs contained in it from the cache.
+ */
+
+#define MAX_AGE 60
+#define CHECK_INTERVAL 10
+#define MAX_OLD 0.333
+
+
+typedef struct {
+ GskVulkanImage *image;
+ int width, height;
+ int x, y, y0;
+ int num_glyphs;
+ GList *dirty_glyphs;
+ guint old_pixels;
+} Atlas;
+
+struct _GskVulkanGlyphCache {
+ GObject parent_instance;
+
+ GdkVulkanContext *vulkan;
+
+ GHashTable *hash_table;
+ GPtrArray *atlases;
+
+ guint64 timestamp;
+};
+
+struct _GskVulkanGlyphCacheClass {
+ GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (GskVulkanGlyphCache, gsk_vulkan_glyph_cache, G_TYPE_OBJECT)
+
+static guint glyph_cache_hash (gconstpointer v);
+static gboolean glyph_cache_equal (gconstpointer v1,
+ gconstpointer v2);
+static void glyph_cache_key_free (gpointer v);
+static void glyph_cache_value_free (gpointer v);
+static void dirty_glyph_free (gpointer v);
+
+static Atlas *
+create_atlas (GskVulkanGlyphCache *cache)
+{
+ Atlas *atlas;
+
+ atlas = g_new0 (Atlas, 1);
+ atlas->width = 512;
+ atlas->height = 512;
+ atlas->y0 = 1;
+ atlas->y = 1;
+ atlas->x = 1;
+ atlas->image = NULL;
+ atlas->num_glyphs = 0;
+ atlas->dirty_glyphs = NULL;
+
+ return atlas;
+}
+
+static void
+free_atlas (gpointer v)
+{
+ Atlas *atlas = v;
+
+ g_clear_object (&atlas->image);
+ g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
+ g_free (atlas);
+}
+
+static void
+gsk_vulkan_glyph_cache_init (GskVulkanGlyphCache *cache)
+{
+ cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
+ glyph_cache_key_free, glyph_cache_value_free);
+ cache->atlases = g_ptr_array_new_with_free_func (free_atlas);
+}
+
+static void
+gsk_vulkan_glyph_cache_finalize (GObject *object)
+{
+ GskVulkanGlyphCache *cache = GSK_VULKAN_GLYPH_CACHE (object);
+
+ g_ptr_array_unref (cache->atlases);
+ g_hash_table_unref (cache->hash_table);
+
+ G_OBJECT_CLASS (gsk_vulkan_glyph_cache_parent_class)->finalize (object);
+}
+
+static void
+gsk_vulkan_glyph_cache_class_init (GskVulkanGlyphCacheClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsk_vulkan_glyph_cache_finalize;
+}
+
+typedef struct {
+ PangoFont *font;
+ PangoGlyph glyph;
+ guint scale; /* times 1024 */
+} GlyphCacheKey;
+
+static gboolean
+glyph_cache_equal (gconstpointer v1, gconstpointer v2)
+{
+ const GlyphCacheKey *key1 = v1;
+ const GlyphCacheKey *key2 = v2;
+
+ return key1->font == key2->font &&
+ key1->glyph == key2->glyph &&
+ key1->scale == key2->scale;
+}
+
+static guint
+glyph_cache_hash (gconstpointer v)
+{
+ const GlyphCacheKey *key = v;
+
+ return GPOINTER_TO_UINT (key->font) ^ key->glyph ^ key->scale;
+}
+
+static void
+glyph_cache_key_free (gpointer v)
+{
+ GlyphCacheKey *f = v;
+
+ g_object_unref (f->font);
+ g_free (f);
+}
+
+static void
+glyph_cache_value_free (gpointer v)
+{
+ g_free (v);
+}
+
+typedef struct {
+ GlyphCacheKey *key;
+ GskVulkanCachedGlyph *value;
+ cairo_surface_t *surface;
+} DirtyGlyph;
+
+static void
+dirty_glyph_free (gpointer v)
+{
+ DirtyGlyph *glyph = v;
+
+ if (glyph->surface)
+ cairo_surface_destroy (glyph->surface);
+ g_free (glyph);
+}
+
+static void
+add_to_cache (GskVulkanGlyphCache *cache,
+ GlyphCacheKey *key,
+ GskVulkanCachedGlyph *value)
+{
+ Atlas *atlas;
+ int i;
+ DirtyGlyph *dirty;
+ int width = value->draw_width * key->scale / 1024;
+ int height = value->draw_height * key->scale / 1024;
+
+ for (i = 0; i < cache->atlases->len; i++)
+ {
+ int x, y, y0;
+
+ atlas = g_ptr_array_index (cache->atlases, i);
+ x = atlas->x;
+ y = atlas->y;
+ y0 = atlas->y0;
+
+ if (atlas->x + width + 1 >= atlas->width)
+ {
+ /* start a new row */
+ y0 = y + 1;
+ x = 1;
+ }
+
+ if (y0 + height + 1 >= atlas->height)
+ continue;
+
+ atlas->y0 = y0;
+ atlas->x = x;
+ atlas->y = y;
+ break;
+ }
+
+ if (i == cache->atlases->len)
+ {
+ atlas = create_atlas (cache);
+ g_ptr_array_add (cache->atlases, atlas);
+ }
+
+ value->tx = (float)atlas->x / atlas->width;
+ value->ty = (float)atlas->y0 / atlas->height;
+ value->tw = (float)width / atlas->width;
+ value->th = (float)height / atlas->height;
+
+ value->texture_index = i;
+
+ dirty = g_new (DirtyGlyph, 1);
+ dirty->key = key;
+ dirty->value = value;
+ atlas->dirty_glyphs = g_list_prepend (atlas->dirty_glyphs, dirty);
+
+ atlas->x = atlas->x + width + 1;
+ atlas->y = MAX (atlas->y, atlas->y0 + height + 1);
+
+ atlas->num_glyphs++;
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_DEBUG_CHECK(GLYPH_CACHE))
+ {
+ g_print ("Glyph cache:\n");
+ for (i = 0; i < cache->atlases->len; i++)
+ {
+ atlas = g_ptr_array_index (cache->atlases, i);
+ g_print ("\tAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
+ i, atlas->width, atlas->height,
+ atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
+ 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
+ atlas->x, atlas->y0, atlas->y);
+ }
+ }
+#endif
+}
+
+static void
+render_glyph (Atlas *atlas,
+ DirtyGlyph *glyph,
+ GskImageRegion *region)
+{
+ GlyphCacheKey *key = glyph->key;
+ GskVulkanCachedGlyph *value = glyph->value;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ PangoGlyphString glyphs;
+ PangoGlyphInfo gi;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ value->draw_width * key->scale / 1024,
+ value->draw_height * key->scale / 1024);
+ cairo_surface_set_device_scale (surface, key->scale / 1024.0, key->scale / 1024.0);
+
+ cr = cairo_create (surface);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+
+ gi.glyph = key->glyph;
+ gi.geometry.width = value->draw_width * 1024;
+ if (key->glyph & PANGO_GLYPH_UNKNOWN_FLAG)
+ gi.geometry.x_offset = 0;
+ else
+ gi.geometry.x_offset = - value->draw_x * 1024;
+ gi.geometry.y_offset = - value->draw_y * 1024;
+
+ glyphs.num_glyphs = 1;
+ glyphs.glyphs = &gi;
+
+ pango_cairo_show_glyph_string (cr, key->font, &glyphs);
+
+ cairo_destroy (cr);
+
+ glyph->surface = surface;
+
+ region->data = cairo_image_surface_get_data (surface);
+ region->width = cairo_image_surface_get_width (surface);
+ region->height = cairo_image_surface_get_height (surface);
+ region->stride = cairo_image_surface_get_stride (surface);
+ region->x = (gsize)(value->tx * atlas->width);
+ region->y = (gsize)(value->ty * atlas->height);
+}
+
+static void
+upload_dirty_glyphs (Atlas *atlas,
+ GskVulkanUploader *uploader)
+{
+ GList *l;
+ guint num_regions;
+ GskImageRegion *regions;
+ int i;
+
+ num_regions = g_list_length (atlas->dirty_glyphs);
+ regions = alloca (sizeof (GskImageRegion) * num_regions);
+
+ for (l = atlas->dirty_glyphs, i = 0; l; l = l->next, i++)
+ render_glyph (atlas, (DirtyGlyph *)l->data, &regions[i]);
+
+ GSK_NOTE (GLYPH_CACHE,
+ g_print ("uploading %d glyphs to cache\n", num_regions));
+
+ gsk_vulkan_image_upload_regions (atlas->image, uploader, num_regions, regions);
+
+ g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
+ atlas->dirty_glyphs = NULL;
+}
+
+GskVulkanGlyphCache *
+gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan)
+{
+ GskVulkanGlyphCache *cache;
+
+ cache = GSK_VULKAN_GLYPH_CACHE (g_object_new (GSK_TYPE_VULKAN_GLYPH_CACHE, NULL));
+ cache->vulkan = vulkan;
+ g_ptr_array_add (cache->atlases, create_atlas (cache));
+
+ return cache;
+}
+
+GskVulkanCachedGlyph *
+gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
+ gboolean create,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale)
+{
+ GlyphCacheKey lookup_key;
+ GskVulkanCachedGlyph *value;
+
+ lookup_key.font = font;
+ lookup_key.glyph = glyph;
+ lookup_key.scale = (guint)(scale * 1024);
+
+ value = g_hash_table_lookup (cache->hash_table, &lookup_key);
+
+ if (value)
+ {
+ if (cache->timestamp - value->timestamp >= MAX_AGE)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
+
+ atlas->old_pixels -= value->draw_width * value->draw_height;
+ value->timestamp = cache->timestamp;
+ }
+ }
+
+ if (create && value == NULL)
+ {
+ GlyphCacheKey *key;
+ PangoRectangle ink_rect;
+
+ key = g_new (GlyphCacheKey, 1);
+ value = g_new0 (GskVulkanCachedGlyph, 1);
+
+ pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ value->draw_x = ink_rect.x;
+ value->draw_y = ink_rect.y;
+ value->draw_width = ink_rect.width;
+ value->draw_height = ink_rect.height;
+ value->timestamp = cache->timestamp;
+
+ key->font = g_object_ref (font);
+ key->glyph = glyph;
+ key->scale = (guint)(scale * 1024);
+
+ if (ink_rect.width > 0 && ink_rect.height > 0)
+ add_to_cache (cache, key, value);
+
+ g_hash_table_insert (cache->hash_table, key, value);
+ }
+
+ return value;
+}
+
+GskVulkanImage *
+gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
+ GskVulkanUploader *uploader,
+ guint index)
+{
+ Atlas *atlas;
+
+ g_return_val_if_fail (index < cache->atlases->len, NULL);
+
+ atlas = g_ptr_array_index (cache->atlases, index);
+
+ if (atlas->image == NULL)
+ atlas->image = gsk_vulkan_image_new_for_atlas (cache->vulkan, atlas->width, atlas->height);
+
+ if (atlas->dirty_glyphs)
+ upload_dirty_glyphs (atlas, uploader);
+
+ return atlas->image;
+}
+
+void
+gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
+{
+ int i, j;
+ guint *drops;
+ guint *shifts;
+ guint len;
+ GHashTableIter iter;
+ GlyphCacheKey *key;
+ GskVulkanCachedGlyph *value;
+ guint dropped = 0;
+
+ cache->timestamp++;
+
+ if (cache->timestamp % CHECK_INTERVAL != 0)
+ return;
+
+ len = cache->atlases->len;
+
+ /* look for glyphs that have grown old since last time */
+ g_hash_table_iter_init (&iter, cache->hash_table);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ guint age;
+
+ age = cache->timestamp - value->timestamp;
+ if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, value->texture_index);
+ atlas->old_pixels += value->draw_width * value->draw_height;
+ }
+ }
+
+ drops = g_alloca (sizeof (guint) * len);
+ shifts = g_alloca (sizeof (guint) * len);
+
+ for (i = 0; i < len; i++)
+ {
+ drops[i] = 0;
+ shifts[i] = i;
+ }
+
+ /* look for atlases to drop, and create a mapping of updated texture indices */
+ for (i = cache->atlases->len - 1; i >= 0; i--)
+ {
+ Atlas *atlas = g_ptr_array_index (cache->atlases, i);
+
+ if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
+ {
+ GSK_NOTE(GLYPH_CACHE,
+ g_print ("Dropping atlas %d (%g.2%% old)\n", i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height)));
+ g_ptr_array_remove_index (cache->atlases, i);
+
+ drops[i] = 1;
+ for (j = i; j + 1 < len; j++)
+ shifts[j + 1] = shifts[j];
+ }
+ }
+
+ /* no atlas dropped, we're done */
+ if (len == cache->atlases->len)
+ return;
+
+ /* purge glyphs and update texture indices */
+ g_hash_table_iter_init (&iter, cache->hash_table);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ if (drops[value->texture_index])
+ {
+ dropped++;
+ g_hash_table_iter_remove (&iter);
+ }
+ else
+ {
+ value->texture_index = shifts[value->texture_index];
+ }
+ }
+
+ GSK_NOTE(GLYPH_CACHE, g_print ("Dropped %d glyphs\n", dropped));
+}
diff --git a/gsk/vulkan/gskvulkanglyphcacheprivate.h b/gsk/vulkan/gskvulkanglyphcacheprivate.h
new file mode 100644
index 0000000000..6cf223f27d
--- /dev/null
+++ b/gsk/vulkan/gskvulkanglyphcacheprivate.h
@@ -0,0 +1,28 @@
+#ifndef __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__
+#define __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__
+
+#include <pango/pango.h>
+#include "gskvulkanrendererprivate.h"
+#include "gskvulkanimageprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_VULKAN_GLYPH_CACHE (gsk_vulkan_glyph_cache_get_type ())
+
+G_DECLARE_FINAL_TYPE(GskVulkanGlyphCache, gsk_vulkan_glyph_cache, GSK, VULKAN_GLYPH_CACHE, GObject)
+
+GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan);
+
+GskVulkanImage * gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
+ GskVulkanUploader *uploader,
+ guint index);
+
+GskVulkanCachedGlyph *gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
+ gboolean create,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale);
+
+void gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache);
+
+#endif /* __GSK_VULKAN_GLYPH_CACHE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanimage.c b/gsk/vulkan/gskvulkanimage.c
new file mode 100644
index 0000000000..8d3bdb42e0
--- /dev/null
+++ b/gsk/vulkan/gskvulkanimage.c
@@ -0,0 +1,820 @@
+#include "config.h"
+
+#include "gskvulkanimageprivate.h"
+
+#include "gskvulkanbufferprivate.h"
+#include "gskvulkanmemoryprivate.h"
+#include "gskvulkanpipelineprivate.h"
+
+#include <string.h>
+
+struct _GskVulkanUploader
+{
+ GdkVulkanContext *vulkan;
+
+ GskVulkanCommandPool *command_pool;
+
+ GArray *before_buffer_barriers;
+ GArray *before_image_barriers;
+ VkCommandBuffer copy_buffer;
+ GArray *after_buffer_barriers;
+ GArray *after_image_barriers;
+
+ GSList *staging_image_free_list;
+ GSList *staging_buffer_free_list;
+};
+
+struct _GskVulkanImage
+{
+ GObject parent_instance;
+
+ GdkVulkanContext *vulkan;
+
+ gsize width;
+ gsize height;
+ VkImageUsageFlags vk_usage;
+ VkImage vk_image;
+ VkImageView vk_image_view;
+ VkImageLayout vk_image_layout;
+ VkAccessFlags vk_access;
+
+ GskVulkanMemory *memory;
+};
+
+G_DEFINE_TYPE (GskVulkanImage, gsk_vulkan_image, G_TYPE_OBJECT)
+
+GskVulkanUploader *
+gsk_vulkan_uploader_new (GdkVulkanContext *context,
+ GskVulkanCommandPool *command_pool)
+{
+ GskVulkanUploader *self;
+
+ self = g_slice_new0 (GskVulkanUploader);
+
+ self->vulkan = g_object_ref (context);
+ self->command_pool = command_pool;
+
+ self->before_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
+ self->after_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
+
+ self->before_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
+ self->after_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
+
+ return self;
+}
+
+void
+gsk_vulkan_uploader_free (GskVulkanUploader *self)
+{
+ gsk_vulkan_uploader_reset (self);
+
+ g_array_unref (self->after_buffer_barriers);
+ g_array_unref (self->before_buffer_barriers);
+ g_array_unref (self->after_image_barriers);
+ g_array_unref (self->before_image_barriers);
+
+ g_object_unref (self->vulkan);
+
+ g_slice_free (GskVulkanUploader, self);
+}
+
+static void
+gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self,
+ gboolean after,
+ GskVulkanImage *image,
+ VkImageLayout new_layout,
+ VkAccessFlags new_access)
+{
+ GArray *array;
+ VkImageMemoryBarrier barrier = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = image->vk_access,
+ .dstAccessMask = new_access,
+ .oldLayout = image->vk_image_layout,
+ .newLayout = new_layout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = image->vk_image,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ }
+ };
+
+ if (after)
+ array = self->after_image_barriers;
+ else
+ array = self->before_image_barriers;
+
+ g_array_append_val (array, barrier);
+
+ image->vk_image_layout = new_layout;
+ image->vk_access = new_access;
+}
+
+static void
+gsk_vulkan_uploader_add_buffer_barrier (GskVulkanUploader *self,
+ gboolean after,
+ const VkBufferMemoryBarrier *barrier)
+{
+ GArray *array;
+
+ if (after)
+ array = self->after_buffer_barriers;
+ else
+ array = self->before_buffer_barriers;
+
+ g_array_append_val (array, *barrier);
+}
+
+static VkCommandBuffer
+gsk_vulkan_uploader_get_copy_buffer (GskVulkanUploader *self)
+{
+ if (self->copy_buffer == VK_NULL_HANDLE)
+ self->copy_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
+
+ return self->copy_buffer;
+}
+
+void
+gsk_vulkan_uploader_upload (GskVulkanUploader *self)
+{
+ if (self->before_buffer_barriers->len > 0 || self->before_image_barriers->len > 0)
+ {
+ VkCommandBuffer command_buffer;
+
+ command_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
+ vkCmdPipelineBarrier (command_buffer,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0,
+ 0, NULL,
+ self->before_buffer_barriers->len, (VkBufferMemoryBarrier *) self->before_buffer_barriers->data,
+ self->before_image_barriers->len, (VkImageMemoryBarrier *) self->before_image_barriers->data);
+ gsk_vulkan_command_pool_submit_buffer (self->command_pool, command_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
+ g_array_set_size (self->before_buffer_barriers, 0);
+ g_array_set_size (self->before_image_barriers, 0);
+ }
+
+ /* append these to existing buffer */
+ if (self->after_buffer_barriers->len > 0 || self->after_image_barriers->len > 0)
+ {
+ VkCommandBuffer command_buffer = gsk_vulkan_uploader_get_copy_buffer (self);
+ vkCmdPipelineBarrier (command_buffer,
+ VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ 0,
+ 0, NULL,
+ self->after_buffer_barriers->len, (VkBufferMemoryBarrier *) self->after_buffer_barriers->data,
+ self->after_image_barriers->len, (VkImageMemoryBarrier *) self->after_image_barriers->data);
+ g_array_set_size (self->after_buffer_barriers, 0);
+ g_array_set_size (self->after_image_barriers, 0);
+ }
+
+ if (self->copy_buffer != VK_NULL_HANDLE)
+ {
+ gsk_vulkan_command_pool_submit_buffer (self->command_pool, self->copy_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
+ self->copy_buffer = VK_NULL_HANDLE;
+ }
+}
+
+void
+gsk_vulkan_uploader_reset (GskVulkanUploader *self)
+{
+ g_array_set_size (self->before_image_barriers, 0);
+ self->copy_buffer = VK_NULL_HANDLE;
+ g_array_set_size (self->after_image_barriers, 0);
+
+ g_slist_free_full (self->staging_image_free_list, g_object_unref);
+ self->staging_image_free_list = NULL;
+ g_slist_free_full (self->staging_buffer_free_list, (GDestroyNotify) gsk_vulkan_buffer_free);
+ self->staging_buffer_free_list = NULL;
+}
+
+static GskVulkanImage *
+gsk_vulkan_image_new (GdkVulkanContext *context,
+ gsize width,
+ gsize height,
+ VkImageTiling tiling,
+ VkImageUsageFlags usage,
+ VkImageLayout layout,
+ VkAccessFlags access,
+ VkMemoryPropertyFlags memory)
+{
+ VkMemoryRequirements requirements;
+ GskVulkanImage *self;
+
+ self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
+
+ self->vulkan = g_object_ref (context);
+ self->width = width;
+ self->height = height;
+ self->vk_usage = usage;
+ self->vk_image_layout = layout;
+ self->vk_access = access;
+
+ GSK_VK_CHECK (vkCreateImage, gdk_vulkan_context_get_device (context),
+ &(VkImageCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .flags = 0,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = VK_FORMAT_B8G8R8A8_UNORM,
+ .extent = { width, height, 1 },
+ .mipLevels = 1,
+ .arrayLayers = 1,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = tiling,
+ .usage = usage,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .initialLayout = self->vk_image_layout,
+ },
+ NULL,
+ &self->vk_image);
+
+ vkGetImageMemoryRequirements (gdk_vulkan_context_get_device (context),
+ self->vk_image,
+ &requirements);
+
+ self->memory = gsk_vulkan_memory_new (context,
+ requirements.memoryTypeBits,
+ memory,
+ requirements.size);
+
+ GSK_VK_CHECK (vkBindImageMemory, gdk_vulkan_context_get_device (context),
+ self->vk_image,
+ gsk_vulkan_memory_get_device_memory (self->memory),
+ 0);
+ return self;
+}
+
+static void
+gsk_vulkan_image_upload_data (GskVulkanImage *self,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize data_stride)
+{
+ VkImageSubresource image_res;
+ VkSubresourceLayout image_layout;
+ gsize mem_stride;
+ guchar *mem;
+
+ image_res.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ image_res.mipLevel = 0;
+ image_res.arrayLayer = 0;
+
+ mem_stride = width * 4;
+ vkGetImageSubresourceLayout (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_image, &image_res, &image_layout);
+
+ mem = gsk_vulkan_memory_map (self->memory) + image_layout.offset;
+
+ if (image_layout.rowPitch == width * 4 && data_stride == mem_stride)
+ {
+ memcpy (mem, data, data_stride * height);
+ }
+ else
+ {
+ for (gsize i = 0; i < height; i++)
+ {
+ memcpy (mem + i * image_layout.rowPitch, data + i * data_stride, width * 4);
+ }
+ }
+
+ gsk_vulkan_memory_unmap (self->memory);
+}
+
+static void
+gsk_vulkan_image_ensure_view (GskVulkanImage *self,
+ VkFormat format)
+{
+ if (self->vk_image_view == VK_NULL_HANDLE)
+ GSK_VK_CHECK (vkCreateImageView, gdk_vulkan_context_get_device (self->vulkan),
+ &(VkImageViewCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = self->vk_image,
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = format,
+ .components = {
+ .r = VK_COMPONENT_SWIZZLE_R,
+ .g = VK_COMPONENT_SWIZZLE_G,
+ .b = VK_COMPONENT_SWIZZLE_B,
+ .a = VK_COMPONENT_SWIZZLE_A,
+ },
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ },
+ NULL,
+ &self->vk_image_view);
+}
+
+static GskVulkanImage *
+gsk_vulkan_image_new_from_data_via_staging_buffer (GskVulkanUploader *uploader,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize stride)
+{
+ GskVulkanImage *self;
+ GskVulkanBuffer *staging;
+ gsize buffer_size = width * height * 4;
+ guchar *mem;
+
+ staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, buffer_size);
+ mem = gsk_vulkan_buffer_map (staging);
+
+ if (stride == width * 4)
+ {
+ memcpy (mem, data, stride * height);
+ }
+ else
+ {
+ for (gsize i = 0; i < height; i++)
+ {
+ memcpy (mem + i * width * 4, data + i * stride, width * 4);
+ }
+ }
+
+ gsk_vulkan_buffer_unmap (staging);
+
+ gsk_vulkan_uploader_add_buffer_barrier (uploader,
+ FALSE,
+ &(VkBufferMemoryBarrier) {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = gsk_vulkan_buffer_get_buffer(staging),
+ .offset = 0,
+ .size = buffer_size,
+ });
+
+ self = gsk_vulkan_image_new (uploader->vulkan,
+ width,
+ height,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ FALSE,
+ self,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT);
+
+ vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
+ gsk_vulkan_buffer_get_buffer (staging),
+ self->vk_image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1,
+ (VkBufferImageCopy[1]) {
+ {
+ .bufferOffset = 0,
+ .imageSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .imageOffset = { 0, 0, 0 },
+ .imageExtent = {
+ .width = width,
+ .height = height,
+ .depth = 1
+ }
+ }
+ });
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ TRUE,
+ self,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_ACCESS_SHADER_READ_BIT);
+
+ uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+static GskVulkanImage *
+gsk_vulkan_image_new_from_data_via_staging_image (GskVulkanUploader *uploader,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize stride)
+{
+ GskVulkanImage *self, *staging;
+
+ staging = gsk_vulkan_image_new (uploader->vulkan,
+ width,
+ height,
+ VK_IMAGE_TILING_LINEAR,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ VK_IMAGE_LAYOUT_PREINITIALIZED,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+
+ gsk_vulkan_image_upload_data (staging, data, width, height, stride);
+
+ self = gsk_vulkan_image_new (uploader->vulkan,
+ width,
+ height,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ FALSE,
+ staging,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ FALSE,
+ self,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT);
+
+ vkCmdCopyImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
+ staging->vk_image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ self->vk_image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1,
+ &(VkImageCopy) {
+ .srcSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .srcOffset = { 0, 0, 0 },
+ .dstSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .dstOffset = { 0, 0, 0 },
+ .extent = {
+ .width = width,
+ .height = height,
+ .depth = 1
+ }
+ });
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ TRUE,
+ self,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_ACCESS_SHADER_READ_BIT);
+
+ uploader->staging_image_free_list = g_slist_prepend (uploader->staging_image_free_list, staging);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+static GskVulkanImage *
+gsk_vulkan_image_new_from_data_directly (GskVulkanUploader *uploader,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize stride)
+{
+ GskVulkanImage *self;
+
+ self = gsk_vulkan_image_new (uploader->vulkan,
+ width,
+ height,
+ VK_IMAGE_TILING_LINEAR,
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_LAYOUT_PREINITIALIZED,
+ VK_ACCESS_HOST_WRITE_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+
+ gsk_vulkan_image_upload_data (self, data, width, height, stride);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ TRUE,
+ self,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_ACCESS_SHADER_READ_BIT);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+GskVulkanImage *
+gsk_vulkan_image_new_from_data (GskVulkanUploader *uploader,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize stride)
+{
+ if (GSK_RENDER_MODE_CHECK (STAGING_BUFFER))
+ return gsk_vulkan_image_new_from_data_via_staging_buffer (uploader, data, width, height, stride);
+ if (GSK_RENDER_MODE_CHECK (STAGING_IMAGE))
+ return gsk_vulkan_image_new_from_data_via_staging_image (uploader, data, width, height, stride);
+ else
+ return gsk_vulkan_image_new_from_data_directly (uploader, data, width, height, stride);
+}
+
+GskVulkanImage *
+gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
+ VkImage image,
+ VkFormat format,
+ gsize width,
+ gsize height)
+{
+ GskVulkanImage *self;
+
+ self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL);
+
+ self->vulkan = g_object_ref (context);
+ self->width = width;
+ self->height = height;
+ self->vk_image = image;
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+GskVulkanImage *
+gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
+ gsize width,
+ gsize height)
+{
+ GskVulkanImage *self;
+
+
+ self = gsk_vulkan_image_new (context,
+ width,
+ height,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+GskVulkanImage *
+gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
+ gsize width,
+ gsize height)
+{
+ GskVulkanImage *self;
+
+ self = gsk_vulkan_image_new (context,
+ width,
+ height,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ 0,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+GskVulkanImage *
+gsk_vulkan_image_new_for_texture (GdkVulkanContext *context,
+ gsize width,
+ gsize height)
+{
+ GskVulkanImage *self;
+
+ self = gsk_vulkan_image_new (context,
+ width,
+ height,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ 0,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+
+ return self;
+}
+
+GdkTexture *
+gsk_vulkan_image_download (GskVulkanImage *self,
+ GskVulkanUploader *uploader)
+{
+ GskVulkanBuffer *buffer;
+ GdkTexture *texture;
+ guchar *mem;
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ FALSE,
+ self,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT);
+
+ buffer = gsk_vulkan_buffer_new_download (self->vulkan, self->width * self->height * 4);
+
+ vkCmdCopyImageToBuffer (gsk_vulkan_uploader_get_copy_buffer (uploader),
+ self->vk_image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ gsk_vulkan_buffer_get_buffer (buffer),
+ 1,
+ (VkBufferImageCopy[1]) {
+ {
+ .bufferOffset = 0,
+ .imageSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1
+ },
+ .imageOffset = { 0, 0, 0 },
+ .imageExtent = {
+ .width = self->width,
+ .height = self->height,
+ .depth = 1
+ }
+ }
+ });
+
+ gsk_vulkan_uploader_upload (uploader);
+
+ GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
+
+ mem = gsk_vulkan_buffer_map (buffer);
+ texture = gdk_texture_new_for_data (mem, self->width, self->height, self->width * 4);
+ gsk_vulkan_buffer_unmap (buffer);
+ gsk_vulkan_buffer_free (buffer);
+
+ return texture;
+}
+
+void
+gsk_vulkan_image_upload_regions (GskVulkanImage *self,
+ GskVulkanUploader *uploader,
+ guint num_regions,
+ GskImageRegion *regions)
+{
+ GskVulkanBuffer *staging;
+ guchar *mem;
+ guchar *m;
+ gsize size;
+ gsize offset;
+ VkBufferImageCopy *bufferImageCopy;
+
+ size = 0;
+ for (int i = 0; i < num_regions; i++)
+ size += regions[i].width * regions[i].height * 4;
+
+ staging = gsk_vulkan_buffer_new_staging (uploader->vulkan, size);
+ mem = gsk_vulkan_buffer_map (staging);
+
+ bufferImageCopy = alloca (sizeof (VkBufferImageCopy) * num_regions);
+ memset (bufferImageCopy, 0, sizeof (VkBufferImageCopy) * num_regions);
+
+ offset = 0;
+ for (int i = 0; i < num_regions; i++)
+ {
+ m = mem + offset;
+ if (regions[i].stride == regions[i].width * 4)
+ {
+ memcpy (m, regions[i].data, regions[i].stride * regions[i].height);
+ }
+ else
+ {
+ for (gsize r = 0; r < regions[i].height; i++)
+ memcpy (m + r * regions[i].width * 4, regions[i].data + r * regions[i].stride, regions[i].width * 4);
+ }
+
+ bufferImageCopy[i].bufferOffset = offset;
+ bufferImageCopy[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ bufferImageCopy[i].imageSubresource.mipLevel = 0;
+ bufferImageCopy[i].imageSubresource.baseArrayLayer = 0;
+ bufferImageCopy[i].imageSubresource.layerCount = 1;
+ bufferImageCopy[i].imageOffset.x = regions[i].x;
+ bufferImageCopy[i].imageOffset.y = regions[i].y;
+ bufferImageCopy[i].imageOffset.z = 0;
+ bufferImageCopy[i].imageExtent.width = regions[i].width;
+ bufferImageCopy[i].imageExtent.height = regions[i].height;
+ bufferImageCopy[i].imageExtent.depth = 1;
+
+ offset += regions[i].width * regions[i].height * 4;
+ }
+
+ gsk_vulkan_buffer_unmap (staging);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ FALSE,
+ self,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT);
+
+ vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
+ gsk_vulkan_buffer_get_buffer (staging),
+ self->vk_image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ num_regions,
+ bufferImageCopy);
+
+ gsk_vulkan_uploader_add_image_barrier (uploader,
+ TRUE,
+ self,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_ACCESS_SHADER_READ_BIT);
+
+ uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging);
+
+ gsk_vulkan_image_ensure_view (self, VK_FORMAT_B8G8R8A8_UNORM);
+}
+
+static void
+gsk_vulkan_image_finalize (GObject *object)
+{
+ GskVulkanImage *self = GSK_VULKAN_IMAGE (object);
+
+ if (self->vk_image_view != VK_NULL_HANDLE)
+ {
+ vkDestroyImageView (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_image_view,
+ NULL);
+ }
+
+ /* memory is NULL for for_swapchain() images, where we don't own
+ * the VkImage */
+ if (self->memory)
+ {
+ vkDestroyImage (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_image,
+ NULL);
+
+ gsk_vulkan_memory_free (self->memory);
+ }
+
+ g_object_unref (self->vulkan);
+
+ G_OBJECT_CLASS (gsk_vulkan_image_parent_class)->finalize (object);
+}
+
+static void
+gsk_vulkan_image_class_init (GskVulkanImageClass *klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_image_finalize;
+}
+
+static void
+gsk_vulkan_image_init (GskVulkanImage *self)
+{
+}
+
+gsize
+gsk_vulkan_image_get_width (GskVulkanImage *self)
+{
+ return self->width;
+}
+
+gsize
+gsk_vulkan_image_get_height (GskVulkanImage *self)
+{
+ return self->height;
+}
+
+VkImage
+gsk_vulkan_image_get_image (GskVulkanImage *self)
+{
+ return self->vk_image;
+}
+
+VkImageView
+gsk_vulkan_image_get_image_view (GskVulkanImage *self)
+{
+ return self->vk_image_view;
+}
diff --git a/gsk/vulkan/gskvulkanimageprivate.h b/gsk/vulkan/gskvulkanimageprivate.h
new file mode 100644
index 0000000000..e775fe0b2c
--- /dev/null
+++ b/gsk/vulkan/gskvulkanimageprivate.h
@@ -0,0 +1,67 @@
+#ifndef __GSK_VULKAN_IMAGE_PRIVATE_H__
+#define __GSK_VULKAN_IMAGE_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+#include "gskvulkancommandpoolprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanUploader GskVulkanUploader;
+
+#define GSK_TYPE_VULKAN_IMAGE (gsk_vulkan_image_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanImage, gsk_vulkan_image, GSK, VULKAN_IMAGE, GObject)
+
+GskVulkanUploader * gsk_vulkan_uploader_new (GdkVulkanContext *context,
+ GskVulkanCommandPool *command_pool);
+void gsk_vulkan_uploader_free (GskVulkanUploader *self);
+
+void gsk_vulkan_uploader_reset (GskVulkanUploader *self);
+void gsk_vulkan_uploader_upload (GskVulkanUploader *self);
+
+GskVulkanImage * gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
+ VkImage image,
+ VkFormat format,
+ gsize width,
+ gsize height);
+GskVulkanImage * gsk_vulkan_image_new_from_data (GskVulkanUploader *uploader,
+ guchar *data,
+ gsize width,
+ gsize height,
+ gsize stride);
+
+typedef struct {
+ guchar *data;
+ gsize width;
+ gsize height;
+ gsize stride;
+ gsize x;
+ gsize y;
+} GskImageRegion;
+
+void gsk_vulkan_image_upload_regions (GskVulkanImage *image,
+ GskVulkanUploader *uploader,
+ guint num_regions,
+ GskImageRegion *regions);
+GskVulkanImage * gsk_vulkan_image_new_for_framebuffer (GdkVulkanContext *context,
+ gsize width,
+ gsize height);
+GskVulkanImage * gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
+ gsize width,
+ gsize height);
+GskVulkanImage * gsk_vulkan_image_new_for_texture (GdkVulkanContext *context,
+ gsize width,
+ gsize height);
+
+GdkTexture * gsk_vulkan_image_download (GskVulkanImage *self,
+ GskVulkanUploader *uploader);
+
+gsize gsk_vulkan_image_get_width (GskVulkanImage *self);
+gsize gsk_vulkan_image_get_height (GskVulkanImage *self);
+VkImage gsk_vulkan_image_get_image (GskVulkanImage *self);
+VkImageView gsk_vulkan_image_get_image_view (GskVulkanImage *self);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_IMAGE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanlineargradientpipeline.c b/gsk/vulkan/gskvulkanlineargradientpipeline.c
new file mode 100644
index 0000000000..058a2f8416
--- /dev/null
+++ b/gsk/vulkan/gskvulkanlineargradientpipeline.c
@@ -0,0 +1,225 @@
+#include "config.h"
+
+#include "gskvulkanlineargradientpipelineprivate.h"
+
+struct _GskVulkanLinearGradientPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanLinearGradientInstance GskVulkanLinearGradientInstance;
+
+struct _GskVulkanLinearGradientInstance
+{
+ float rect[4];
+ float start[2];
+ float end[2];
+ gint32 repeating;
+ gint32 stop_count;
+ float offsets[GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS];
+ float colors[GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS][4];
+};
+
+G_DEFINE_TYPE (GskVulkanLinearGradientPipeline, gsk_vulkan_linear_gradient_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_linear_gradient_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanLinearGradientInstance),
+ .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_R32G32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, start),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, end),
+ },
+ {
+ .location = 3,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SINT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, repeating),
+ },
+ {
+ .location = 4,
+ .binding = 0,
+ .format = VK_FORMAT_R32_SINT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count),
+ },
+ {
+ .location = 5,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offsets),
+ },
+ {
+ .location = 6,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offsets) + sizeof (float) * 4,
+ },
+ {
+ .location = 7,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[0]),
+ },
+ {
+ .location = 8,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[1]),
+ },
+ {
+ .location = 9,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[2]),
+ },
+ {
+ .location = 10,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[3]),
+ },
+ {
+ .location = 11,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[4]),
+ },
+ {
+ .location = 12,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[5]),
+ },
+ {
+ .location = 13,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[6]),
+ },
+ {
+ .location = 14,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[7]),
+ }
+ };
+ 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_linear_gradient_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanLinearGradientPipeline *self = GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_linear_gradient_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_linear_gradient_pipeline_class_init (GskVulkanLinearGradientPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_linear_gradient_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_linear_gradient_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_linear_gradient_pipeline_init (GskVulkanLinearGradientPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_linear_gradient_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_LINEAR_GRADIENT_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_linear_gradient_pipeline_count_vertex_data (GskVulkanLinearGradientPipeline *pipeline)
+{
+ return sizeof (GskVulkanLinearGradientInstance);
+}
+
+void
+gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradientPipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_point_t *start,
+ const graphene_point_t *end,
+ gboolean repeating,
+ gsize n_stops,
+ const GskColorStop *stops)
+{
+ GskVulkanLinearGradientInstance *instance = (GskVulkanLinearGradientInstance *) data;
+ gsize i;
+
+ if (n_stops > GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS)
+ {
+ g_warning ("Only %u color stops supported.", GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS);
+ n_stops = GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS;
+ }
+ instance->rect[0] = rect->origin.x;
+ instance->rect[1] = rect->origin.y;
+ instance->rect[2] = rect->size.width;
+ instance->rect[3] = rect->size.height;
+ instance->start[0] = start->x;
+ instance->start[1] = start->y;
+ instance->end[0] = end->x;
+ instance->end[1] = end->y;
+ instance->repeating = repeating;
+ instance->stop_count = n_stops;
+ for (i = 0; i < n_stops; i++)
+ {
+ instance->offsets[i] = stops[i].offset;
+ instance->colors[i][0] = stops[i].color.red;
+ instance->colors[i][1] = stops[i].color.green;
+ instance->colors[i][2] = stops[i].color.blue;
+ instance->colors[i][3] = stops[i].color.alpha;
+ }
+}
+
+gsize
+gsk_vulkan_linear_gradient_pipeline_draw (GskVulkanLinearGradientPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h b/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h
new file mode 100644
index 0000000000..71b6559dc2
--- /dev/null
+++ b/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h
@@ -0,0 +1,42 @@
+#ifndef __GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskrendernode.h"
+
+G_BEGIN_DECLS
+
+#define GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS 8
+
+typedef struct _GskVulkanLinearGradientPipelineLayout GskVulkanLinearGradientPipelineLayout;
+
+#define GSK_TYPE_VULKAN_LINEAR_GRADIENT_PIPELINE (gsk_vulkan_linear_gradient_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanLinearGradientPipeline, gsk_vulkan_linear_gradient_pipeline, GSK, VULKAN_LINEAR_GRADIENT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_linear_gradient_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_linear_gradient_pipeline_count_vertex_data
+ (GskVulkanLinearGradientPipeline*pipeline);
+void gsk_vulkan_linear_gradient_pipeline_collect_vertex_data
+ (GskVulkanLinearGradientPipeline*pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_point_t *start,
+ const graphene_point_t *end,
+ gboolean repeating,
+ gsize n_stops,
+ const GskColorStop *stops);
+gsize gsk_vulkan_linear_gradient_pipeline_draw (GskVulkanLinearGradientPipeline*pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanmemory.c b/gsk/vulkan/gskvulkanmemory.c
new file mode 100644
index 0000000000..1ff46757d4
--- /dev/null
+++ b/gsk/vulkan/gskvulkanmemory.c
@@ -0,0 +1,95 @@
+#include "config.h"
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanmemoryprivate.h"
+
+struct _GskVulkanMemory
+{
+ GdkVulkanContext *vulkan;
+
+ gsize size;
+
+ VkDeviceMemory vk_memory;
+};
+
+GskVulkanMemory *
+gsk_vulkan_memory_new (GdkVulkanContext *context,
+ uint32_t allowed_types,
+ VkMemoryPropertyFlags flags,
+ gsize size)
+{
+ VkPhysicalDeviceMemoryProperties properties;
+ GskVulkanMemory *self;
+ uint32_t i;
+
+ self = g_slice_new0 (GskVulkanMemory);
+
+ self->vulkan = g_object_ref (context);
+ self->size = size;
+
+ vkGetPhysicalDeviceMemoryProperties (gdk_vulkan_context_get_physical_device (context),
+ &properties);
+
+ for (i = 0; i < properties.memoryTypeCount; i++)
+ {
+ if (!(allowed_types & (1 << i)))
+ continue;
+
+ if ((properties.memoryTypes[i].propertyFlags & flags) == flags)
+ break;
+ }
+
+ g_assert (i < properties.memoryTypeCount);
+
+ GSK_VK_CHECK (vkAllocateMemory, gdk_vulkan_context_get_device (context),
+ &(VkMemoryAllocateInfo) {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = size,
+ .memoryTypeIndex = i
+ },
+ NULL,
+ &self->vk_memory);
+
+ return self;
+}
+
+void
+gsk_vulkan_memory_free (GskVulkanMemory *self)
+{
+ vkFreeMemory (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_memory,
+ NULL);
+
+ g_object_unref (self->vulkan);
+
+ g_slice_free (GskVulkanMemory, self);
+}
+
+VkDeviceMemory
+gsk_vulkan_memory_get_device_memory (GskVulkanMemory *self)
+{
+ return self->vk_memory;
+}
+
+guchar *
+gsk_vulkan_memory_map (GskVulkanMemory *self)
+{
+ void *data;
+
+ GSK_VK_CHECK (vkMapMemory, gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_memory,
+ 0,
+ self->size,
+ 0,
+ &data);
+
+ return data;
+}
+
+void
+gsk_vulkan_memory_unmap (GskVulkanMemory *self)
+{
+ vkUnmapMemory (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_memory);
+}
+
diff --git a/gsk/vulkan/gskvulkanmemoryprivate.h b/gsk/vulkan/gskvulkanmemoryprivate.h
new file mode 100644
index 0000000000..010c133018
--- /dev/null
+++ b/gsk/vulkan/gskvulkanmemoryprivate.h
@@ -0,0 +1,23 @@
+#ifndef __GSK_VULKAN_MEMORY_PRIVATE_H__
+#define __GSK_VULKAN_MEMORY_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanMemory GskVulkanMemory;
+
+GskVulkanMemory * gsk_vulkan_memory_new (GdkVulkanContext *context,
+ uint32_t allowed_types,
+ VkMemoryPropertyFlags properties,
+ gsize size);
+void gsk_vulkan_memory_free (GskVulkanMemory *memory);
+
+VkDeviceMemory gsk_vulkan_memory_get_device_memory (GskVulkanMemory *self);
+
+guchar * gsk_vulkan_memory_map (GskVulkanMemory *self);
+void gsk_vulkan_memory_unmap (GskVulkanMemory *self);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_MEMORY_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanpipeline.c b/gsk/vulkan/gskvulkanpipeline.c
new file mode 100644
index 0000000000..38d5a5795f
--- /dev/null
+++ b/gsk/vulkan/gskvulkanpipeline.c
@@ -0,0 +1,189 @@
+#include "config.h"
+
+#include "gskvulkanpipelineprivate.h"
+
+#include "gskvulkanpushconstantsprivate.h"
+#include "gskvulkanshaderprivate.h"
+
+#include <graphene.h>
+
+typedef struct _GskVulkanPipelinePrivate GskVulkanPipelinePrivate;
+
+struct _GskVulkanPipelinePrivate
+{
+ GObject parent_instance;
+
+ GdkVulkanContext *context;
+
+ VkPipeline pipeline;
+ VkPipelineLayout layout;
+
+ GskVulkanShader *vertex_shader;
+ GskVulkanShader *fragment_shader;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GskVulkanPipeline, gsk_vulkan_pipeline, G_TYPE_OBJECT)
+
+static void
+gsk_vulkan_pipeline_finalize (GObject *gobject)
+{
+ GskVulkanPipelinePrivate *priv = gsk_vulkan_pipeline_get_instance_private (GSK_VULKAN_PIPELINE (gobject));
+ VkDevice device;
+
+ device = gdk_vulkan_context_get_device (priv->context);
+
+ vkDestroyPipeline (device,
+ priv->pipeline,
+ NULL);
+
+ g_clear_pointer (&priv->fragment_shader, gsk_vulkan_shader_free);
+ g_clear_pointer (&priv->vertex_shader, gsk_vulkan_shader_free);
+
+ G_OBJECT_CLASS (gsk_vulkan_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_pipeline_class_init (GskVulkanPipelineClass *klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_pipeline_finalize;
+}
+
+static void
+gsk_vulkan_pipeline_init (GskVulkanPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_pipeline_new (GType pipeline_type,
+ GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass 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);
+}
+
+GskVulkanPipeline *
+gsk_vulkan_pipeline_new_full (GType pipeline_type,
+ GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ 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 (render_pass != VK_NULL_HANDLE, NULL);
+
+ self = g_object_new (pipeline_type, NULL);
+
+ priv = gsk_vulkan_pipeline_get_instance_private (self);
+
+ device = gdk_vulkan_context_get_device (context);
+
+ 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);
+
+ GSK_VK_CHECK (vkCreateGraphicsPipelines, device,
+ VK_NULL_HANDLE,
+ 1,
+ &(VkGraphicsPipelineCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .stageCount = 2,
+ .pStages = (VkPipelineShaderStageCreateInfo[2]) {
+ GST_VULKAN_SHADER_STAGE_CREATE_INFO (priv->vertex_shader),
+ GST_VULKAN_SHADER_STAGE_CREATE_INFO (priv->fragment_shader)
+ },
+ .pVertexInputState = GSK_VULKAN_PIPELINE_GET_CLASS (self)->get_input_state_create_info (self),
+ .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ .primitiveRestartEnable = VK_FALSE,
+ },
+ .pTessellationState = NULL,
+ .pViewportState = &(VkPipelineViewportStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .viewportCount = 1,
+ .scissorCount = 1
+ },
+ .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .depthClampEnable = VK_FALSE,
+ .rasterizerDiscardEnable = VK_FALSE,
+ .polygonMode = VK_POLYGON_MODE_FILL,
+ .cullMode = VK_CULL_MODE_BACK_BIT,
+ .frontFace = VK_FRONT_FACE_CLOCKWISE,
+ .lineWidth = 1.0f,
+ },
+ .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .rasterizationSamples = 1,
+ },
+ .pDepthStencilState = &(VkPipelineDepthStencilStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
+ },
+ .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = (VkPipelineColorBlendAttachmentState []) {
+ {
+ .blendEnable = VK_TRUE,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcColorBlendFactor = srcBlendFactor,
+ .dstColorBlendFactor = dstBlendFactor,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = srcBlendFactor,
+ .dstAlphaBlendFactor = dstBlendFactor,
+ .colorWriteMask = VK_COLOR_COMPONENT_A_BIT
+ | VK_COLOR_COMPONENT_R_BIT
+ | VK_COLOR_COMPONENT_G_BIT
+ | VK_COLOR_COMPONENT_B_BIT
+ },
+ }
+ },
+ .pDynamicState = &(VkPipelineDynamicStateCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .dynamicStateCount = 2,
+ .pDynamicStates = (VkDynamicState[2]) {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR
+ },
+ },
+ .layout = priv->layout,
+ .renderPass = render_pass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = -1,
+ },
+ NULL,
+ &priv->pipeline);
+
+ return self;
+}
+
+VkPipeline
+gsk_vulkan_pipeline_get_pipeline (GskVulkanPipeline *self)
+{
+ GskVulkanPipelinePrivate *priv = gsk_vulkan_pipeline_get_instance_private (self);
+
+ return priv->pipeline;
+}
+
+VkPipelineLayout
+gsk_vulkan_pipeline_get_pipeline_layout (GskVulkanPipeline *self)
+{
+ GskVulkanPipelinePrivate *priv = gsk_vulkan_pipeline_get_instance_private (self);
+
+ return priv->layout;
+}
diff --git a/gsk/vulkan/gskvulkanpipelineprivate.h b/gsk/vulkan/gskvulkanpipelineprivate.h
new file mode 100644
index 0000000000..cd41f822b3
--- /dev/null
+++ b/gsk/vulkan/gskvulkanpipelineprivate.h
@@ -0,0 +1,53 @@
+#ifndef __GSK_VULKAN_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_PIPELINE_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+#include "gskdebugprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_VULKAN_PIPELINE (gsk_vulkan_pipeline_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (GskVulkanPipeline, gsk_vulkan_pipeline, GSK, VULKAN_PIPELINE, GObject)
+
+struct _GskVulkanPipelineClass
+{
+ GObjectClass parent_class;
+
+ const VkPipelineVertexInputStateCreateInfo *
+ (* get_input_state_create_info) (GskVulkanPipeline *self);
+};
+
+static inline VkResult
+gsk_vulkan_handle_result (VkResult res,
+ const char *called_function)
+{
+ if (res != VK_SUCCESS)
+ {
+ GSK_NOTE (VULKAN,g_printerr ("%s(): %s (%d)\n", called_function, gdk_vulkan_strerror (res), res));
+ }
+ return res;
+}
+
+#define GSK_VK_CHECK(func, ...) gsk_vulkan_handle_result (func (__VA_ARGS__), G_STRINGIFY (func))
+
+GskVulkanPipeline * gsk_vulkan_pipeline_new (GType pipeline_type,
+ GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+GskVulkanPipeline * gsk_vulkan_pipeline_new_full (GType pipeline_type,
+ GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass,
+ VkBlendFactor srcBlendFactor,
+ VkBlendFactor dstBlendFactor);
+
+VkPipeline gsk_vulkan_pipeline_get_pipeline (GskVulkanPipeline *self);
+VkPipelineLayout gsk_vulkan_pipeline_get_pipeline_layout (GskVulkanPipeline *self);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanpushconstants.c b/gsk/vulkan/gskvulkanpushconstants.c
new file mode 100644
index 0000000000..dc4ae404b9
--- /dev/null
+++ b/gsk/vulkan/gskvulkanpushconstants.c
@@ -0,0 +1,114 @@
+#include "config.h"
+
+#include "gskvulkanpushconstantsprivate.h"
+
+#include "gskroundedrectprivate.h"
+
+typedef struct _GskVulkanPushConstantsWire GskVulkanPushConstantsWire;
+
+struct _GskVulkanPushConstantsWire
+{
+ struct {
+ float mvp[16];
+ float clip[12];
+ } common;
+};
+
+void
+gsk_vulkan_push_constants_init (GskVulkanPushConstants *constants,
+ const graphene_matrix_t *mvp,
+ const graphene_rect_t *viewport)
+{
+ graphene_matrix_init_from_matrix (&constants->mvp, mvp);
+ gsk_vulkan_clip_init_empty (&constants->clip, viewport);
+}
+
+void
+gsk_vulkan_push_constants_init_copy (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src)
+{
+ *self = *src;
+}
+
+gboolean
+gsk_vulkan_push_constants_transform (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const graphene_matrix_t *transform,
+ const graphene_rect_t *viewport)
+
+{
+ if (!gsk_vulkan_clip_transform (&self->clip, &src->clip, transform, viewport))
+ return FALSE;
+
+ graphene_matrix_multiply (transform, &src->mvp, &self->mvp);
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_push_constants_intersect_rect (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const graphene_rect_t *rect)
+{
+ if (!gsk_vulkan_clip_intersect_rect (&self->clip, &src->clip, rect))
+ return FALSE;
+
+ graphene_matrix_init_from_matrix (&self->mvp, &src->mvp);
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_push_constants_intersect_rounded (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const GskRoundedRect *rect)
+{
+ if (!gsk_vulkan_clip_intersect_rounded_rect (&self->clip, &src->clip, rect))
+ return FALSE;
+
+ graphene_matrix_init_from_matrix (&self->mvp, &src->mvp);
+ return TRUE;
+}
+
+static void
+gsk_vulkan_push_constants_wire_init (GskVulkanPushConstantsWire *wire,
+ const GskVulkanPushConstants *self)
+{
+ graphene_matrix_to_float (&self->mvp, wire->common.mvp);
+ gsk_rounded_rect_to_float (&self->clip.rect, wire->common.clip);
+}
+
+void
+gsk_vulkan_push_constants_push (const GskVulkanPushConstants *self,
+ VkCommandBuffer command_buffer,
+ VkPipelineLayout pipeline_layout)
+{
+ GskVulkanPushConstantsWire wire;
+
+ gsk_vulkan_push_constants_wire_init (&wire, self);
+
+ vkCmdPushConstants (command_buffer,
+ pipeline_layout,
+ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
+ G_STRUCT_OFFSET (GskVulkanPushConstantsWire, common),
+ sizeof (wire.common),
+ &wire.common);
+}
+
+uint32_t
+gsk_vulkan_push_constants_get_range_count (void)
+{
+ return 1;
+}
+
+const VkPushConstantRange *
+gsk_vulkan_push_constants_get_ranges (void)
+{
+ static const VkPushConstantRange ranges[1] = {
+ {
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
+ .offset = G_STRUCT_OFFSET (GskVulkanPushConstantsWire, common),
+ .size = sizeof (((GskVulkanPushConstantsWire *) 0)->common)
+ }
+ };
+
+ return ranges;
+}
diff --git a/gsk/vulkan/gskvulkanpushconstantsprivate.h b/gsk/vulkan/gskvulkanpushconstantsprivate.h
new file mode 100644
index 0000000000..897d9c61fc
--- /dev/null
+++ b/gsk/vulkan/gskvulkanpushconstantsprivate.h
@@ -0,0 +1,45 @@
+#ifndef __GSK_VULKAN_PUSH_CONSTANTS_PRIVATE_H__
+#define __GSK_VULKAN_PUSH_CONSTANTS_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <graphene.h>
+#include "gskvulkanclipprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanPushConstants GskVulkanPushConstants;
+
+struct _GskVulkanPushConstants
+{
+ graphene_matrix_t mvp;
+ GskVulkanClip clip;
+};
+
+const VkPushConstantRange *
+ gsk_vulkan_push_constants_get_ranges (void) G_GNUC_PURE;
+uint32_t gsk_vulkan_push_constants_get_range_count (void) G_GNUC_PURE;
+
+void gsk_vulkan_push_constants_init (GskVulkanPushConstants *constants,
+ const graphene_matrix_t *mvp,
+ const graphene_rect_t *viewport);
+void gsk_vulkan_push_constants_init_copy (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src);
+
+gboolean gsk_vulkan_push_constants_transform (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const graphene_matrix_t *transform,
+ const graphene_rect_t *viewport);
+gboolean gsk_vulkan_push_constants_intersect_rect (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const graphene_rect_t *rect);
+gboolean gsk_vulkan_push_constants_intersect_rounded (GskVulkanPushConstants *self,
+ const GskVulkanPushConstants *src,
+ const GskRoundedRect *rect);
+
+void gsk_vulkan_push_constants_push (const GskVulkanPushConstants *self,
+ VkCommandBuffer command_buffer,
+ VkPipelineLayout pipeline_layout);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_PUSH_CONSTANTS_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanrender.c b/gsk/vulkan/gskvulkanrender.c
new file mode 100644
index 0000000000..5d8b241c12
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrender.c
@@ -0,0 +1,761 @@
+#include "config.h"
+
+#include "gskprivate.h"
+
+#include "gskvulkanrenderprivate.h"
+
+#include "gskrendererprivate.h"
+#include "gskvulkanbufferprivate.h"
+#include "gskvulkancommandpoolprivate.h"
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrenderpassprivate.h"
+
+#include "gskvulkanblendmodepipelineprivate.h"
+#include "gskvulkanblurpipelineprivate.h"
+#include "gskvulkanborderpipelineprivate.h"
+#include "gskvulkanboxshadowpipelineprivate.h"
+#include "gskvulkancolorpipelineprivate.h"
+#include "gskvulkancolortextpipelineprivate.h"
+#include "gskvulkancrossfadepipelineprivate.h"
+#include "gskvulkaneffectpipelineprivate.h"
+#include "gskvulkanlineargradientpipelineprivate.h"
+#include "gskvulkantextpipelineprivate.h"
+#include "gskvulkantexturepipelineprivate.h"
+#include "gskvulkanpushconstantsprivate.h"
+
+#define DESCRIPTOR_POOL_MAXSETS 128
+#define DESCRIPTOR_POOL_MAXSETS_INCREASE 128
+
+struct _GskVulkanRender
+{
+ GskRenderer *renderer;
+ GdkVulkanContext *vulkan;
+
+ int scale_factor;
+ graphene_rect_t viewport;
+ cairo_region_t *clip;
+
+ GHashTable *framebuffers;
+ GskVulkanCommandPool *command_pool;
+ VkFence fence;
+ VkRenderPass render_pass;
+ VkDescriptorSetLayout descriptor_set_layout;
+ VkPipelineLayout pipeline_layout[3]; /* indexed by number of textures */
+ GskVulkanUploader *uploader;
+
+ GHashTable *descriptor_set_indexes;
+ VkDescriptorPool descriptor_pool;
+ uint32_t descriptor_pool_maxsets;
+ VkDescriptorSet *descriptor_sets;
+ gsize n_descriptor_sets;
+ GskVulkanPipeline *pipelines[GSK_VULKAN_N_PIPELINES];
+
+ GskVulkanImage *target;
+
+ VkSampler sampler;
+ VkSampler repeating_sampler;
+
+ GList *render_passes;
+ GSList *cleanup_images;
+
+ GQuark render_pass_counter;
+ GQuark gpu_time_timer;
+};
+
+static void
+gsk_vulkan_render_setup (GskVulkanRender *self,
+ GskVulkanImage *target,
+ const graphene_rect_t *rect)
+{
+ GdkWindow *window = gsk_renderer_get_window (self->renderer);
+
+ self->target = g_object_ref (target);
+
+ if (rect)
+ {
+ self->viewport = *rect;
+ self->scale_factor = 1;
+ self->clip = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
+ 0, 0,
+ gsk_vulkan_image_get_width (target),
+ gsk_vulkan_image_get_height (target)
+ });
+ }
+ else
+ {
+ self->scale_factor = gdk_window_get_scale_factor (gsk_renderer_get_window (self->renderer));
+ self->viewport = GRAPHENE_RECT_INIT (0, 0,
+ gdk_window_get_width (window) * self->scale_factor,
+ gdk_window_get_height (window) * self->scale_factor);
+ self->clip = gdk_drawing_context_get_clip (gsk_renderer_get_drawing_context (self->renderer));
+ }
+}
+
+static guint desc_set_index_hash (gconstpointer v);
+static gboolean desc_set_index_equal (gconstpointer v1, gconstpointer v2);
+
+GskVulkanRender *
+gsk_vulkan_render_new (GskRenderer *renderer,
+ GdkVulkanContext *context)
+{
+ GskVulkanRender *self;
+ VkDevice device;
+
+ self = g_slice_new0 (GskVulkanRender);
+
+ self->vulkan = context;
+ self->renderer = renderer;
+ self->framebuffers = g_hash_table_new (g_direct_hash, g_direct_equal);
+ self->descriptor_set_indexes = g_hash_table_new_full (desc_set_index_hash, desc_set_index_equal, NULL, g_free);
+
+ device = gdk_vulkan_context_get_device (self->vulkan);
+
+ self->command_pool = gsk_vulkan_command_pool_new (self->vulkan);
+ GSK_VK_CHECK (vkCreateFence, device,
+ &(VkFenceCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ .flags = VK_FENCE_CREATE_SIGNALED_BIT
+ },
+ NULL,
+ &self->fence);
+
+ self->descriptor_pool_maxsets = DESCRIPTOR_POOL_MAXSETS;
+ GSK_VK_CHECK (vkCreateDescriptorPool, device,
+ &(VkDescriptorPoolCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = self->descriptor_pool_maxsets,
+ .poolSizeCount = 1,
+ .pPoolSizes = (VkDescriptorPoolSize[1]) {
+ {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = self->descriptor_pool_maxsets
+ }
+ }
+ },
+ NULL,
+ &self->descriptor_pool);
+
+ GSK_VK_CHECK (vkCreateRenderPass, gdk_vulkan_context_get_device (self->vulkan),
+ &(VkRenderPassCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = (VkAttachmentDescription[]) {
+ {
+ .format = gdk_vulkan_context_get_image_format (self->vulkan),
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ }
+ },
+ .subpassCount = 1,
+ .pSubpasses = (VkSubpassDescription []) {
+ {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .inputAttachmentCount = 0,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = (VkAttachmentReference []) {
+ {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ },
+ .pResolveAttachments = (VkAttachmentReference []) {
+ {
+ .attachment = VK_ATTACHMENT_UNUSED,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ },
+ .pDepthStencilAttachment = NULL,
+ }
+ },
+ .dependencyCount = 0
+ },
+ NULL,
+ &self->render_pass);
+
+ GSK_VK_CHECK (vkCreateDescriptorSetLayout, device,
+ &(VkDescriptorSetLayoutCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .bindingCount = 1,
+ .pBindings = (VkDescriptorSetLayoutBinding[1]) {
+ {
+ .binding = 0,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
+ }
+ }
+ },
+ NULL,
+ &self->descriptor_set_layout);
+
+ for (guint i = 0; i < 3; i++)
+ {
+ VkDescriptorSetLayout layouts[3] = {
+ self->descriptor_set_layout,
+ self->descriptor_set_layout,
+ self->descriptor_set_layout
+ };
+
+ GSK_VK_CHECK (vkCreatePipelineLayout, device,
+ &(VkPipelineLayoutCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .setLayoutCount = i,
+ .pSetLayouts = layouts,
+ .pushConstantRangeCount = gsk_vulkan_push_constants_get_range_count (),
+ .pPushConstantRanges = gsk_vulkan_push_constants_get_ranges ()
+ },
+ NULL,
+ &self->pipeline_layout[i]);
+ }
+
+ GSK_VK_CHECK (vkCreateSampler, device,
+ &(VkSamplerCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .magFilter = VK_FILTER_LINEAR,
+ .minFilter = VK_FILTER_LINEAR,
+ .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+ .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+ .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
+ .unnormalizedCoordinates = VK_FALSE,
+ .maxAnisotropy = 1.0,
+ },
+ NULL,
+ &self->sampler);
+
+ GSK_VK_CHECK (vkCreateSampler, device,
+ &(VkSamplerCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .magFilter = VK_FILTER_LINEAR,
+ .minFilter = VK_FILTER_LINEAR,
+ .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
+ .unnormalizedCoordinates = VK_FALSE,
+ .maxAnisotropy = 1.0,
+ },
+ NULL,
+ &self->repeating_sampler);
+
+ self->uploader = gsk_vulkan_uploader_new (self->vulkan, self->command_pool);
+
+#ifdef G_ENABLE_DEBUG
+ self->render_pass_counter = g_quark_from_static_string ("render-passes");
+ self->gpu_time_timer = g_quark_from_static_string ("gpu-time");
+#endif
+
+ return self;
+}
+
+typedef struct {
+ VkFramebuffer framebuffer;
+} HashFramebufferEntry;
+
+static void
+gsk_vulkan_render_remove_framebuffer_from_image (gpointer data,
+ GObject *image)
+{
+ GskVulkanRender *self = data;
+ HashFramebufferEntry *fb;
+
+ fb = g_hash_table_lookup (self->framebuffers, image);
+ g_hash_table_remove (self->framebuffers, image);
+
+ vkDestroyFramebuffer (gdk_vulkan_context_get_device (self->vulkan),
+ fb->framebuffer,
+ NULL);
+
+ g_slice_free (HashFramebufferEntry, fb);
+}
+
+VkFramebuffer
+gsk_vulkan_render_get_framebuffer (GskVulkanRender *self,
+ GskVulkanImage *image)
+{
+ HashFramebufferEntry *fb;
+
+ fb = g_hash_table_lookup (self->framebuffers, image);
+ if (fb)
+ return fb->framebuffer;
+
+ fb = g_slice_new0 (HashFramebufferEntry);
+ GSK_VK_CHECK (vkCreateFramebuffer, gdk_vulkan_context_get_device (self->vulkan),
+ &(VkFramebufferCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = self->render_pass,
+ .attachmentCount = 1,
+ .pAttachments = (VkImageView[1]) {
+ gsk_vulkan_image_get_image_view (image)
+ },
+ .width = gsk_vulkan_image_get_width (image),
+ .height = gsk_vulkan_image_get_height (image),
+ .layers = 1
+ },
+ NULL,
+ &fb->framebuffer);
+ g_hash_table_insert (self->framebuffers, image, fb);
+ g_object_weak_ref (G_OBJECT (image), gsk_vulkan_render_remove_framebuffer_from_image, self);
+
+ return fb->framebuffer;
+}
+
+void
+gsk_vulkan_render_add_cleanup_image (GskVulkanRender *self,
+ GskVulkanImage *image)
+{
+ self->cleanup_images = g_slist_prepend (self->cleanup_images, image);
+}
+
+void
+gsk_vulkan_render_add_render_pass (GskVulkanRender *self,
+ GskVulkanRenderPass *pass)
+{
+ self->render_passes = g_list_prepend (self->render_passes, pass);
+
+#ifdef G_ENABLE_DEBUG
+ gsk_profiler_counter_inc (gsk_renderer_get_profiler (self->renderer), self->render_pass_counter);
+#endif
+}
+
+void
+gsk_vulkan_render_add_node (GskVulkanRender *self,
+ GskRenderNode *node)
+{
+ GskVulkanRenderPass *pass;
+ graphene_matrix_t mv;
+
+ graphene_matrix_init_scale (&mv, self->scale_factor, self->scale_factor, 1.0);
+
+ pass = gsk_vulkan_render_pass_new (self->vulkan,
+ self->target,
+ self->scale_factor,
+ &mv,
+ &self->viewport,
+ self->clip,
+ VK_NULL_HANDLE);
+
+ gsk_vulkan_render_add_render_pass (self, pass);
+
+ gsk_vulkan_render_pass_add (pass, self, node);
+}
+
+void
+gsk_vulkan_render_upload (GskVulkanRender *self)
+{
+ GList *l;
+
+ /* gsk_vulkan_render_pass_upload may call gsk_vulkan_render_add_node_for_texture,
+ * prepending new render passes to the list. Therefore, we walk the list from
+ * the end.
+ */
+ for (l = g_list_last (self->render_passes); l; l = l->prev)
+ {
+ GskVulkanRenderPass *pass = l->data;
+ gsk_vulkan_render_pass_upload (pass, self, self->uploader);
+ }
+
+ gsk_vulkan_uploader_upload (self->uploader);
+}
+
+GskVulkanPipeline *
+gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
+ GskVulkanPipelineType type)
+{
+ static const struct {
+ const char *name;
+ guint num_textures;
+ GskVulkanPipeline * (* create_func) (GdkVulkanContext *context, VkPipelineLayout layout, const char *name, VkRenderPass render_pass);
+ } pipeline_info[GSK_VULKAN_N_PIPELINES] = {
+ { "texture", 1, gsk_vulkan_texture_pipeline_new },
+ { "texture-clip", 1, gsk_vulkan_texture_pipeline_new },
+ { "texture-clip-rounded", 1, gsk_vulkan_texture_pipeline_new },
+ { "color", 0, gsk_vulkan_color_pipeline_new },
+ { "color-clip", 0, gsk_vulkan_color_pipeline_new },
+ { "color-clip-rounded", 0, gsk_vulkan_color_pipeline_new },
+ { "linear", 0, gsk_vulkan_linear_gradient_pipeline_new },
+ { "linear-clip", 0, gsk_vulkan_linear_gradient_pipeline_new },
+ { "linear-clip-rounded", 0, gsk_vulkan_linear_gradient_pipeline_new },
+ { "color-matrix", 1, gsk_vulkan_effect_pipeline_new },
+ { "color-matrix-clip", 1, gsk_vulkan_effect_pipeline_new },
+ { "color-matrix-clip-rounded", 1, gsk_vulkan_effect_pipeline_new },
+ { "border", 0, gsk_vulkan_border_pipeline_new },
+ { "border-clip", 0, gsk_vulkan_border_pipeline_new },
+ { "border-clip-rounded", 0, gsk_vulkan_border_pipeline_new },
+ { "inset-shadow", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "inset-shadow-clip", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "inset-shadow-clip-rounded", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "outset-shadow", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "outset-shadow-clip", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "outset-shadow-clip-rounded", 0, gsk_vulkan_box_shadow_pipeline_new },
+ { "blur", 1, gsk_vulkan_blur_pipeline_new },
+ { "blur-clip", 1, gsk_vulkan_blur_pipeline_new },
+ { "blur-clip-rounded", 1, gsk_vulkan_blur_pipeline_new },
+ { "mask", 1, gsk_vulkan_text_pipeline_new },
+ { "mask-clip", 1, gsk_vulkan_text_pipeline_new },
+ { "mask-clip-rounded", 1, gsk_vulkan_text_pipeline_new },
+ { "texture", 1, gsk_vulkan_color_text_pipeline_new },
+ { "texture-clip", 1, gsk_vulkan_color_text_pipeline_new },
+ { "texture-clip-rounded", 1, gsk_vulkan_color_text_pipeline_new },
+ { "crossfade", 2, gsk_vulkan_cross_fade_pipeline_new },
+ { "crossfade-clip", 2, gsk_vulkan_cross_fade_pipeline_new },
+ { "crossfade-clip-rounded", 2, gsk_vulkan_cross_fade_pipeline_new },
+ { "blendmode", 2, gsk_vulkan_blend_mode_pipeline_new },
+ { "blendmode-clip", 2, gsk_vulkan_blend_mode_pipeline_new },
+ { "blendmode-clip-rounded", 2, gsk_vulkan_blend_mode_pipeline_new },
+ };
+
+ g_return_val_if_fail (type < GSK_VULKAN_N_PIPELINES, NULL);
+
+ if (self->pipelines[type] == NULL)
+ self->pipelines[type] = pipeline_info[type].create_func (self->vulkan,
+ self->pipeline_layout[pipeline_info[type].num_textures],
+ pipeline_info[type].name,
+ self->render_pass);
+
+ return self->pipelines[type];
+}
+
+VkDescriptorSet
+gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self,
+ gsize id)
+{
+ g_assert (id < self->n_descriptor_sets);
+
+ return self->descriptor_sets[id];
+}
+
+typedef struct {
+ gsize index;
+ GskVulkanImage *image;
+ gboolean repeat;
+} HashDescriptorSetIndexEntry;
+
+static guint
+desc_set_index_hash (gconstpointer v)
+{
+ const HashDescriptorSetIndexEntry *e = v;
+
+ return GPOINTER_TO_UINT (e->image) + e->repeat;
+}
+
+static gboolean
+desc_set_index_equal (gconstpointer v1, gconstpointer v2)
+{
+ const HashDescriptorSetIndexEntry *e1 = v1;
+ const HashDescriptorSetIndexEntry *e2 = v2;
+
+ return e1->image == e2->image && e1->repeat == e2->repeat;
+}
+
+gsize
+gsk_vulkan_render_reserve_descriptor_set (GskVulkanRender *self,
+ GskVulkanImage *source,
+ gboolean repeat)
+{
+ HashDescriptorSetIndexEntry lookup;
+ HashDescriptorSetIndexEntry *entry;
+
+ g_assert (source != NULL);
+
+ lookup.image = source;
+ lookup.repeat = repeat;
+
+ entry = g_hash_table_lookup (self->descriptor_set_indexes, &lookup);
+ if (entry)
+ return entry->index;
+
+ entry = g_new (HashDescriptorSetIndexEntry, 1);
+ entry->image = source;
+ entry->repeat = repeat;
+ entry->index = g_hash_table_size (self->descriptor_set_indexes);
+ g_hash_table_add (self->descriptor_set_indexes, entry);
+
+ return entry->index;
+}
+
+static void
+gsk_vulkan_render_prepare_descriptor_sets (GskVulkanRender *self)
+{
+ GHashTableIter iter;
+ gpointer key;
+ VkDevice device;
+ GList *l;
+ guint i, needed_sets;
+
+ device = gdk_vulkan_context_get_device (self->vulkan);
+
+ for (l = self->render_passes; l; l = l->next)
+ {
+ GskVulkanRenderPass *pass = l->data;
+ gsk_vulkan_render_pass_reserve_descriptor_sets (pass, self);
+ }
+
+ needed_sets = g_hash_table_size (self->descriptor_set_indexes);
+ if (needed_sets > self->n_descriptor_sets)
+ {
+ if (needed_sets > self->descriptor_pool_maxsets)
+ {
+ guint added_sets = needed_sets - self->descriptor_pool_maxsets;
+ added_sets = added_sets + DESCRIPTOR_POOL_MAXSETS_INCREASE - 1;
+ added_sets -= added_sets % DESCRIPTOR_POOL_MAXSETS_INCREASE;
+
+ vkDestroyDescriptorPool (device,
+ self->descriptor_pool,
+ NULL);
+ self->descriptor_pool_maxsets += added_sets;
+ GSK_VK_CHECK (vkCreateDescriptorPool, device,
+ &(VkDescriptorPoolCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = self->descriptor_pool_maxsets,
+ .poolSizeCount = 1,
+ .pPoolSizes = (VkDescriptorPoolSize[1]) {
+ {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = self->descriptor_pool_maxsets
+ }
+ }
+ },
+ NULL,
+ &self->descriptor_pool);
+ }
+ else
+ {
+ GSK_VK_CHECK (vkResetDescriptorPool, device,
+ self->descriptor_pool,
+ 0);
+ }
+
+ self->n_descriptor_sets = needed_sets;
+ self->descriptor_sets = g_renew (VkDescriptorSet, self->descriptor_sets, needed_sets);
+ }
+
+ VkDescriptorSetLayout *layouts = g_newa (VkDescriptorSetLayout, needed_sets);
+ for (i = 0; i < needed_sets; i++)
+ layouts[i] = self->descriptor_set_layout;
+
+ GSK_VK_CHECK (vkAllocateDescriptorSets, device,
+ &(VkDescriptorSetAllocateInfo) {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = self->descriptor_pool,
+ .descriptorSetCount = needed_sets,
+ .pSetLayouts = layouts
+ },
+ self->descriptor_sets);
+
+ g_hash_table_iter_init (&iter, self->descriptor_set_indexes);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ HashDescriptorSetIndexEntry *entry = key;
+ GskVulkanImage *image = entry->image;
+ gsize id = entry->index;
+ gboolean repeat = entry->repeat;
+
+ vkUpdateDescriptorSets (device,
+ 1,
+ (VkWriteDescriptorSet[1]) {
+ {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = self->descriptor_sets[id],
+ .dstBinding = 0,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &(VkDescriptorImageInfo) {
+ .sampler = repeat ? self->repeating_sampler : self->sampler,
+ .imageView = gsk_vulkan_image_get_image_view (image),
+ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ }
+ }
+ },
+ 0, NULL);
+ }
+}
+
+void
+gsk_vulkan_render_draw (GskVulkanRender *self)
+{
+ GList *l;
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_RENDER_MODE_CHECK (SYNC))
+ gsk_profiler_timer_begin (gsk_renderer_get_profiler (self->renderer), self->gpu_time_timer);
+#endif
+
+ gsk_vulkan_render_prepare_descriptor_sets (self);
+
+ for (l = self->render_passes; l; l = l->next)
+ {
+ GskVulkanRenderPass *pass = l->data;
+ VkCommandBuffer command_buffer;
+ gsize wait_semaphore_count;
+ gsize signal_semaphore_count;
+ VkSemaphore *wait_semaphores;
+ VkSemaphore *signal_semaphores;
+
+ wait_semaphore_count = gsk_vulkan_render_pass_get_wait_semaphores (pass, &wait_semaphores);
+ signal_semaphore_count = gsk_vulkan_render_pass_get_signal_semaphores (pass, &signal_semaphores);
+
+ command_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
+
+ gsk_vulkan_render_pass_draw (pass, self, 3, self->pipeline_layout, command_buffer);
+
+ gsk_vulkan_command_pool_submit_buffer (self->command_pool,
+ command_buffer,
+ wait_semaphore_count,
+ wait_semaphores,
+ signal_semaphore_count,
+ signal_semaphores,
+ l->next != NULL ? VK_NULL_HANDLE : self->fence);
+ }
+
+ if (GSK_RENDER_MODE_CHECK (SYNC))
+ {
+ GskProfiler *profiler;
+ gint64 gpu_time;
+
+ GSK_VK_CHECK (vkWaitForFences, gdk_vulkan_context_get_device (self->vulkan),
+ 1,
+ &self->fence,
+ VK_TRUE,
+ INT64_MAX);
+
+ profiler = gsk_renderer_get_profiler (self->renderer);
+ gpu_time = gsk_profiler_timer_end (profiler, self->gpu_time_timer);
+ gsk_profiler_timer_set (profiler, self->gpu_time_timer, gpu_time);
+ }
+}
+
+GdkTexture *
+gsk_vulkan_render_download_target (GskVulkanRender *self)
+{
+ gsk_vulkan_uploader_reset (self->uploader);
+
+ return gsk_vulkan_image_download (self->target, self->uploader);
+}
+
+static void
+gsk_vulkan_render_cleanup (GskVulkanRender *self)
+{
+ VkDevice device = gdk_vulkan_context_get_device (self->vulkan);
+
+ /* XXX: Wait for fence here or just in reset()? */
+ GSK_VK_CHECK (vkWaitForFences, device,
+ 1,
+ &self->fence,
+ VK_TRUE,
+ INT64_MAX);
+
+ GSK_VK_CHECK (vkResetFences, device,
+ 1,
+ &self->fence);
+
+ gsk_vulkan_uploader_reset (self->uploader);
+
+ gsk_vulkan_command_pool_reset (self->command_pool);
+
+ g_hash_table_remove_all (self->descriptor_set_indexes);
+ GSK_VK_CHECK (vkResetDescriptorPool, device,
+ self->descriptor_pool,
+ 0);
+
+ g_list_free_full (self->render_passes, (GDestroyNotify) gsk_vulkan_render_pass_free);
+ self->render_passes = NULL;
+ g_slist_free_full (self->cleanup_images, g_object_unref);
+ self->cleanup_images = NULL;
+
+ g_clear_pointer (&self->clip, cairo_region_destroy);
+ g_clear_object (&self->target);
+}
+
+void
+gsk_vulkan_render_free (GskVulkanRender *self)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ VkDevice device;
+ guint i;
+
+ gsk_vulkan_render_cleanup (self);
+
+ device = gdk_vulkan_context_get_device (self->vulkan);
+
+ g_hash_table_iter_init (&iter, self->framebuffers);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ HashFramebufferEntry *fb = value;
+
+ vkDestroyFramebuffer (gdk_vulkan_context_get_device (self->vulkan),
+ fb->framebuffer,
+ NULL);
+ g_slice_free (HashFramebufferEntry, fb);
+ g_object_weak_unref (G_OBJECT (key), gsk_vulkan_render_remove_framebuffer_from_image, self);
+ g_hash_table_iter_remove (&iter);
+ }
+ g_hash_table_unref (self->framebuffers);
+
+ for (i = 0; i < GSK_VULKAN_N_PIPELINES; i++)
+ g_clear_object (&self->pipelines[i]);
+
+ g_clear_pointer (&self->uploader, gsk_vulkan_uploader_free);
+
+ for (i = 0; i < 3; i++)
+ vkDestroyPipelineLayout (device,
+ self->pipeline_layout[i],
+ NULL);
+
+ vkDestroyRenderPass (device,
+ self->render_pass,
+ NULL);
+
+ vkDestroyDescriptorPool (device,
+ self->descriptor_pool,
+ NULL);
+ g_free (self->descriptor_sets);
+ g_hash_table_unref (self->descriptor_set_indexes);
+
+ vkDestroyDescriptorSetLayout (device,
+ self->descriptor_set_layout,
+ NULL);
+
+ vkDestroyFence (device,
+ self->fence,
+ NULL);
+
+ vkDestroySampler (device,
+ self->sampler,
+ NULL);
+
+ vkDestroySampler (device,
+ self->repeating_sampler,
+ NULL);
+
+ gsk_vulkan_command_pool_free (self->command_pool);
+
+ g_slice_free (GskVulkanRender, self);
+}
+
+gboolean
+gsk_vulkan_render_is_busy (GskVulkanRender *self)
+{
+ return vkGetFenceStatus (gdk_vulkan_context_get_device (self->vulkan), self->fence) != VK_SUCCESS;
+}
+
+void
+gsk_vulkan_render_reset (GskVulkanRender *self,
+ GskVulkanImage *target,
+ const graphene_rect_t *rect)
+{
+ gsk_vulkan_render_cleanup (self);
+
+ gsk_vulkan_render_setup (self, target, rect);
+}
+
+GskRenderer *
+gsk_vulkan_render_get_renderer (GskVulkanRender *self)
+{
+ return self->renderer;
+}
diff --git a/gsk/vulkan/gskvulkanrenderer.c b/gsk/vulkan/gskvulkanrenderer.c
new file mode 100644
index 0000000000..82c25ecd53
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrenderer.c
@@ -0,0 +1,375 @@
+#include "config.h"
+
+#include "gskvulkanrendererprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskprivate.h"
+#include "gskrendererprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gskvulkanbufferprivate.h"
+#include "gskvulkanimageprivate.h"
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrenderprivate.h"
+#include "gskvulkanglyphcacheprivate.h"
+
+#include "gdk/gdktextureprivate.h"
+
+#include <graphene.h>
+
+typedef struct _GskVulkanTextureData GskVulkanTextureData;
+
+struct _GskVulkanTextureData {
+ GdkTexture *texture;
+ GskVulkanImage *image;
+ GskVulkanRenderer *renderer;
+};
+
+#ifdef G_ENABLE_DEBUG
+typedef struct {
+ GQuark frames;
+ GQuark render_passes;
+ GQuark fallback_pixels;
+ GQuark texture_pixels;
+} ProfileCounters;
+
+typedef struct {
+ GQuark cpu_time;
+ GQuark gpu_time;
+} ProfileTimers;
+#endif
+
+struct _GskVulkanRenderer
+{
+ GskRenderer parent_instance;
+
+ GdkVulkanContext *vulkan;
+
+ guint n_targets;
+ GskVulkanImage **targets;
+
+ GskVulkanRender *render;
+
+ GSList *textures;
+
+ GskVulkanGlyphCache *glyph_cache;
+
+#ifdef G_ENABLE_DEBUG
+ ProfileCounters profile_counters;
+ ProfileTimers profile_timers;
+#endif
+};
+
+struct _GskVulkanRendererClass
+{
+ GskRendererClass parent_class;
+};
+
+G_DEFINE_TYPE (GskVulkanRenderer, gsk_vulkan_renderer, GSK_TYPE_RENDERER)
+
+static void
+gsk_vulkan_renderer_free_targets (GskVulkanRenderer *self)
+{
+ guint i;
+
+ for (i = 0; i < self->n_targets; i++)
+ {
+ g_object_unref (self->targets[i]);
+ }
+
+ g_clear_pointer (&self->targets, g_free);
+ self->n_targets = 0;
+}
+
+static void
+gsk_vulkan_renderer_update_images_cb (GdkVulkanContext *context,
+ GskVulkanRenderer *self)
+{
+ GdkWindow *window;
+ gint scale_factor;
+ gsize width, height;
+ guint i;
+
+ gsk_vulkan_renderer_free_targets (self);
+
+ self->n_targets = gdk_vulkan_context_get_n_images (context);
+ self->targets = g_new (GskVulkanImage *, self->n_targets);
+
+ window = gsk_renderer_get_window (GSK_RENDERER (self));
+ scale_factor = gdk_window_get_scale_factor (window);
+ width = gdk_window_get_width (window) * scale_factor;
+ height = gdk_window_get_height (window) * scale_factor;
+
+ for (i = 0; i < self->n_targets; i++)
+ {
+ self->targets[i] = gsk_vulkan_image_new_for_swapchain (self->vulkan,
+ gdk_vulkan_context_get_image (context, i),
+ gdk_vulkan_context_get_image_format (self->vulkan),
+ width, height);
+ }
+}
+
+static gboolean
+gsk_vulkan_renderer_realize (GskRenderer *renderer,
+ GdkWindow *window,
+ GError **error)
+{
+ GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
+
+ self->vulkan = gdk_window_create_vulkan_context (window, error);
+ if (self->vulkan == NULL)
+ return FALSE;
+
+ g_signal_connect (self->vulkan,
+ "images-updated",
+ G_CALLBACK (gsk_vulkan_renderer_update_images_cb),
+ self);
+ gsk_vulkan_renderer_update_images_cb (self->vulkan, self);
+
+ self->render = gsk_vulkan_render_new (renderer, self->vulkan);
+
+ self->glyph_cache = gsk_vulkan_glyph_cache_new (self->vulkan);
+
+ return TRUE;
+}
+
+static void
+gsk_vulkan_renderer_unrealize (GskRenderer *renderer)
+{
+ GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
+ GSList *l;
+
+ g_clear_object (&self->glyph_cache);
+
+ for (l = self->textures; l; l = l->next)
+ {
+ GskVulkanTextureData *data = l->data;
+
+ data->renderer = NULL;
+ gdk_texture_clear_render_data (data->texture);
+ }
+ g_clear_pointer (&self->textures, (GDestroyNotify) g_slist_free);
+
+ g_clear_pointer (&self->render, gsk_vulkan_render_free);
+
+ gsk_vulkan_renderer_free_targets (self);
+ g_signal_handlers_disconnect_by_func(self->vulkan,
+ gsk_vulkan_renderer_update_images_cb,
+ self);
+
+ g_clear_object (&self->vulkan);
+}
+
+static GdkTexture *
+gsk_vulkan_renderer_render_texture (GskRenderer *renderer,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport)
+{
+ GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
+ GskVulkanRender *render;
+ GskVulkanImage *image;
+ GdkTexture *texture;
+#ifdef G_ENABLE_DEBUG
+ GskProfiler *profiler;
+ gint64 cpu_time;
+#endif
+
+#ifdef G_ENABLE_DEBUG
+ profiler = gsk_renderer_get_profiler (renderer);
+ gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0);
+ gsk_profiler_counter_set (profiler, self->profile_counters.texture_pixels, 0);
+ gsk_profiler_counter_set (profiler, self->profile_counters.render_passes, 0);
+ gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
+#endif
+
+ render = gsk_vulkan_render_new (renderer, self->vulkan);
+
+ image = gsk_vulkan_image_new_for_framebuffer (self->vulkan,
+ ceil (viewport->size.width),
+ ceil (viewport->size.height));
+
+ gsk_vulkan_render_reset (render, image, viewport);
+
+ gsk_vulkan_render_add_node (render, root);
+
+ gsk_vulkan_render_upload (render);
+
+ gsk_vulkan_render_draw (render);
+
+ texture = gsk_vulkan_render_download_target (render);
+
+ g_object_unref (image);
+ gsk_vulkan_render_free (render);
+
+#ifdef G_ENABLE_DEBUG
+ cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
+ gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
+
+ gsk_profiler_push_samples (profiler);
+#endif
+
+ return texture;
+}
+
+static void
+gsk_vulkan_renderer_render (GskRenderer *renderer,
+ GskRenderNode *root)
+{
+ GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
+ GskVulkanRender *render;
+#ifdef G_ENABLE_DEBUG
+ GskProfiler *profiler;
+ gint64 cpu_time;
+#endif
+
+#ifdef G_ENABLE_DEBUG
+ profiler = gsk_renderer_get_profiler (renderer);
+ gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0);
+ gsk_profiler_counter_set (profiler, self->profile_counters.texture_pixels, 0);
+ gsk_profiler_counter_set (profiler, self->profile_counters.render_passes, 0);
+ gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
+#endif
+
+ render = self->render;
+
+ gsk_vulkan_render_reset (render, self->targets[gdk_vulkan_context_get_draw_index (self->vulkan)], NULL);
+
+ gsk_vulkan_render_add_node (render, root);
+
+ gsk_vulkan_render_upload (render);
+
+ gsk_vulkan_render_draw (render);
+
+#ifdef G_ENABLE_DEBUG
+ gsk_profiler_counter_inc (profiler, self->profile_counters.frames);
+
+ cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
+ gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
+
+ gsk_profiler_push_samples (profiler);
+#endif
+}
+
+static GdkDrawingContext *
+gsk_vulkan_renderer_begin_draw_frame (GskRenderer *renderer,
+ const cairo_region_t *region)
+{
+ GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
+ GdkDrawingContext *result;
+
+ result = gdk_window_begin_draw_frame (gsk_renderer_get_window (renderer),
+ GDK_DRAW_CONTEXT (self->vulkan),
+ region);
+
+ return result;
+}
+
+static void
+gsk_vulkan_renderer_class_init (GskVulkanRendererClass *klass)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
+
+ renderer_class->realize = gsk_vulkan_renderer_realize;
+ renderer_class->unrealize = gsk_vulkan_renderer_unrealize;
+ renderer_class->render = gsk_vulkan_renderer_render;
+ renderer_class->render_texture = gsk_vulkan_renderer_render_texture;
+ renderer_class->begin_draw_frame = gsk_vulkan_renderer_begin_draw_frame;
+}
+
+static void
+gsk_vulkan_renderer_init (GskVulkanRenderer *self)
+{
+#ifdef G_ENABLE_DEBUG
+ GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
+#endif
+
+ gsk_ensure_resources ();
+
+#ifdef G_ENABLE_DEBUG
+ self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE);
+ self->profile_counters.render_passes = gsk_profiler_add_counter (profiler, "render-passes", "Render passes", FALSE);
+ self->profile_counters.fallback_pixels = gsk_profiler_add_counter (profiler, "fallback-pixels", "Fallback pixels", TRUE);
+ self->profile_counters.texture_pixels = gsk_profiler_add_counter (profiler, "texture-pixels", "Texture pixels", TRUE);
+
+ self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE);
+ if (GSK_RENDER_MODE_CHECK (SYNC))
+ self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE);
+#endif
+}
+
+static void
+gsk_vulkan_renderer_clear_texture (gpointer p)
+{
+ GskVulkanTextureData *data = p;
+
+ if (data->renderer != NULL)
+ data->renderer->textures = g_slist_remove (data->renderer->textures, data);
+
+ g_object_unref (data->image);
+
+ g_slice_free (GskVulkanTextureData, data);
+}
+
+GskVulkanImage *
+gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
+ GdkTexture *texture,
+ GskVulkanUploader *uploader)
+{
+ GskVulkanTextureData *data;
+ cairo_surface_t *surface;
+ GskVulkanImage *image;
+
+ data = gdk_texture_get_render_data (texture, self);
+ if (data)
+ return g_object_ref (data->image);
+
+ surface = gdk_texture_download_surface (texture);
+ image = gsk_vulkan_image_new_from_data (uploader,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ cairo_image_surface_get_stride (surface));
+ cairo_surface_destroy (surface);
+
+ data = g_slice_new0 (GskVulkanTextureData);
+ data->image = image;
+ data->texture = texture;
+ data->renderer = self;
+
+ if (gdk_texture_set_render_data (texture, self, data, gsk_vulkan_renderer_clear_texture))
+ {
+ g_object_ref (data->image);
+ self->textures = g_slist_prepend (self->textures, data);
+ }
+ else
+ {
+ g_slice_free (GskVulkanTextureData, data);
+ }
+
+ return image;
+}
+
+guint
+gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *self,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale)
+{
+ return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, TRUE, font, glyph, scale)->texture_index;
+}
+
+GskVulkanImage *
+gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
+ GskVulkanUploader *uploader,
+ guint index)
+{
+ return g_object_ref (gsk_vulkan_glyph_cache_get_glyph_image (self->glyph_cache, uploader, index));
+}
+
+GskVulkanCachedGlyph *
+gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale)
+{
+ return gsk_vulkan_glyph_cache_lookup (self->glyph_cache, FALSE, font, glyph, scale);
+}
diff --git a/gsk/vulkan/gskvulkanrendererprivate.h b/gsk/vulkan/gskvulkanrendererprivate.h
new file mode 100644
index 0000000000..1e7d78a7ed
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrendererprivate.h
@@ -0,0 +1,62 @@
+#ifndef __GSK_VULKAN_RENDERER_PRIVATE_H__
+#define __GSK_VULKAN_RENDERER_PRIVATE_H__
+
+#include <vulkan/vulkan.h>
+#include <gsk/gskrenderer.h>
+
+#include "gskvulkanimageprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_VULKAN_RENDERER (gsk_vulkan_renderer_get_type ())
+
+#define GSK_VULKAN_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_VULKAN_RENDERER, GskVulkanRenderer))
+#define GSK_IS_VULKAN_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_VULKAN_RENDERER))
+#define GSK_VULKAN_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_VULKAN_RENDERER, GskVulkanRendererClass))
+#define GSK_IS_VULKAN_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_VULKAN_RENDERER))
+#define GSK_VULKAN_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_VULKAN_RENDERER, GskVulkanRendererClass))
+
+typedef struct _GskVulkanRenderer GskVulkanRenderer;
+typedef struct _GskVulkanRendererClass GskVulkanRendererClass;
+
+GType gsk_vulkan_renderer_get_type (void) G_GNUC_CONST;
+
+GskVulkanImage * gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
+ GdkTexture *texture,
+ GskVulkanUploader *uploader);
+
+typedef struct
+{
+ guint texture_index;
+
+ float tx;
+ float ty;
+ float tw;
+ float th;
+
+ int draw_x;
+ int draw_y;
+ int draw_width;
+ int draw_height;
+
+ guint64 timestamp;
+} GskVulkanCachedGlyph;
+
+guint gsk_vulkan_renderer_cache_glyph (GskVulkanRenderer *renderer,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale);
+
+GskVulkanImage * gsk_vulkan_renderer_ref_glyph_image (GskVulkanRenderer *self,
+ GskVulkanUploader *uploader,
+ guint index);
+
+GskVulkanCachedGlyph * gsk_vulkan_renderer_get_cached_glyph (GskVulkanRenderer *self,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale);
+
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_RENDERER_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c
new file mode 100644
index 0000000000..83fff2d5e7
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrenderpass.c
@@ -0,0 +1,1942 @@
+#include "config.h"
+
+#include "gskvulkanrenderpassprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskprofilerprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gskrenderer.h"
+#include "gskrendererprivate.h"
+#include "gskroundedrectprivate.h"
+#include "gskvulkanblendmodepipelineprivate.h"
+#include "gskvulkanblurpipelineprivate.h"
+#include "gskvulkanborderpipelineprivate.h"
+#include "gskvulkanboxshadowpipelineprivate.h"
+#include "gskvulkanclipprivate.h"
+#include "gskvulkancolorpipelineprivate.h"
+#include "gskvulkancolortextpipelineprivate.h"
+#include "gskvulkancrossfadepipelineprivate.h"
+#include "gskvulkaneffectpipelineprivate.h"
+#include "gskvulkanlineargradientpipelineprivate.h"
+#include "gskvulkantextpipelineprivate.h"
+#include "gskvulkantexturepipelineprivate.h"
+#include "gskvulkanimageprivate.h"
+#include "gskvulkanpushconstantsprivate.h"
+#include "gskvulkanrendererprivate.h"
+#include "gskprivate.h"
+
+#include <cairo-ft.h>
+
+#define ORTHO_NEAR_PLANE -10000
+#define ORTHO_FAR_PLANE 10000
+
+typedef union _GskVulkanOp GskVulkanOp;
+typedef struct _GskVulkanOpRender GskVulkanOpRender;
+typedef struct _GskVulkanOpText GskVulkanOpText;
+typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants;
+
+typedef enum {
+ /* GskVulkanOpRender */
+ GSK_VULKAN_OP_FALLBACK,
+ GSK_VULKAN_OP_FALLBACK_CLIP,
+ GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP,
+ GSK_VULKAN_OP_SURFACE,
+ GSK_VULKAN_OP_TEXTURE,
+ GSK_VULKAN_OP_COLOR,
+ GSK_VULKAN_OP_LINEAR_GRADIENT,
+ GSK_VULKAN_OP_OPACITY,
+ GSK_VULKAN_OP_BLUR,
+ GSK_VULKAN_OP_COLOR_MATRIX,
+ GSK_VULKAN_OP_BORDER,
+ GSK_VULKAN_OP_INSET_SHADOW,
+ GSK_VULKAN_OP_OUTSET_SHADOW,
+ GSK_VULKAN_OP_REPEAT,
+ GSK_VULKAN_OP_CROSS_FADE,
+ GSK_VULKAN_OP_BLEND_MODE,
+ /* GskVulkanOpText */
+ GSK_VULKAN_OP_TEXT,
+ GSK_VULKAN_OP_COLOR_TEXT,
+ /* GskVulkanOpPushConstants */
+ GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS,
+} GskVulkanOpType;
+
+/* render ops with 0, 1 or 2 sources */
+struct _GskVulkanOpRender
+{
+ GskVulkanOpType type;
+ GskRenderNode *node; /* node that's the source of this op */
+ GskVulkanPipeline *pipeline; /* pipeline to use */
+ GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
+ GskVulkanImage *source; /* source image to render */
+ GskVulkanImage *source2; /* second source image to render (if relevant) */
+ gsize vertex_offset; /* offset into vertex buffer */
+ gsize vertex_count; /* number of vertices */
+ gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
+ gsize descriptor_set_index2; /* descriptor index for the second source (if relevant) */
+ graphene_rect_t source_rect; /* area that source maps to */
+ graphene_rect_t source2_rect; /* area that source2 maps to */
+};
+
+struct _GskVulkanOpText
+{
+ GskVulkanOpType type;
+ GskRenderNode *node; /* node that's the source of this op */
+ GskVulkanPipeline *pipeline; /* pipeline to use */
+ GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
+ GskVulkanImage *source; /* source image to render */
+ gsize vertex_offset; /* offset into vertex buffer */
+ gsize vertex_count; /* number of vertices */
+ gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
+ guint texture_index; /* index of the texture in the glyph cache */
+ guint start_glyph; /* the first glyph in nodes glyphstring that we render */
+ guint num_glyphs; /* number of *non-empty* glyphs (== instances) we render */
+ float scale;
+};
+
+struct _GskVulkanOpPushConstants
+{
+ GskVulkanOpType type;
+ GskRenderNode *node; /* node that's the source of this op */
+ GskVulkanPushConstants constants; /* new constants to push */
+};
+
+union _GskVulkanOp
+{
+ GskVulkanOpType type;
+ GskVulkanOpRender render;
+ GskVulkanOpText text;
+ GskVulkanOpPushConstants constants;
+};
+
+struct _GskVulkanRenderPass
+{
+ GdkVulkanContext *vulkan;
+
+ GArray *render_ops;
+
+ GskVulkanImage *target;
+ int scale_factor;
+ graphene_rect_t viewport;
+ cairo_region_t *clip;
+ graphene_matrix_t mv;
+ graphene_matrix_t p;
+
+ VkRenderPass render_pass;
+ VkSemaphore signal_semaphore;
+ GArray *wait_semaphores;
+ GskVulkanBuffer *vertex_data;
+
+ GQuark fallback_pixels;
+ GQuark texture_pixels;
+};
+
+GskVulkanRenderPass *
+gsk_vulkan_render_pass_new (GdkVulkanContext *context,
+ GskVulkanImage *target,
+ int scale_factor,
+ graphene_matrix_t *mv,
+ graphene_rect_t *viewport,
+ cairo_region_t *clip,
+ VkSemaphore signal_semaphore)
+{
+ GskVulkanRenderPass *self;
+ VkImageLayout final_layout;
+
+ self = g_slice_new0 (GskVulkanRenderPass);
+ self->vulkan = g_object_ref (context);
+ self->render_ops = g_array_new (FALSE, FALSE, sizeof (GskVulkanOp));
+
+ self->target = g_object_ref (target);
+ self->scale_factor = scale_factor;
+ self->clip = cairo_region_copy (clip);
+ self->viewport = *viewport;
+
+ self->mv = *mv;
+ graphene_matrix_init_ortho (&self->p,
+ viewport->origin.x, viewport->origin.x + viewport->size.width,
+ viewport->origin.y, viewport->origin.y + viewport->size.height,
+ ORTHO_NEAR_PLANE,
+ ORTHO_FAR_PLANE);
+
+ if (signal_semaphore != VK_NULL_HANDLE) // this is a dependent pass
+ final_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ else
+ final_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ GSK_VK_CHECK (vkCreateRenderPass, gdk_vulkan_context_get_device (self->vulkan),
+ &(VkRenderPassCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = (VkAttachmentDescription[]) {
+ {
+ .format = gdk_vulkan_context_get_image_format (self->vulkan),
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = final_layout
+ }
+ },
+ .subpassCount = 1,
+ .pSubpasses = (VkSubpassDescription []) {
+ {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .inputAttachmentCount = 0,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = (VkAttachmentReference []) {
+ {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ },
+ .pResolveAttachments = (VkAttachmentReference []) {
+ {
+ .attachment = VK_ATTACHMENT_UNUSED,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ },
+ .pDepthStencilAttachment = NULL,
+ }
+ },
+ .dependencyCount = 0
+ },
+ NULL,
+ &self->render_pass);
+
+ self->signal_semaphore = signal_semaphore;
+ self->wait_semaphores = g_array_new (FALSE, FALSE, sizeof (VkSemaphore));
+ self->vertex_data = NULL;
+
+#ifdef G_ENABLE_DEBUG
+ self->fallback_pixels = g_quark_from_static_string ("fallback-pixels");
+ self->texture_pixels = g_quark_from_static_string ("texture-pixels");
+#endif
+
+ return self;
+}
+
+void
+gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
+{
+ g_array_unref (self->render_ops);
+ g_object_unref (self->vulkan);
+ g_object_unref (self->target);
+ cairo_region_destroy (self->clip);
+ vkDestroyRenderPass (gdk_vulkan_context_get_device (self->vulkan),
+ self->render_pass,
+ NULL);
+ if (self->vertex_data)
+ gsk_vulkan_buffer_free (self->vertex_data);
+ if (self->signal_semaphore != VK_NULL_HANDLE)
+ vkDestroySemaphore (gdk_vulkan_context_get_device (self->vulkan),
+ self->signal_semaphore,
+ NULL);
+ g_array_unref (self->wait_semaphores);
+
+
+ g_slice_free (GskVulkanRenderPass, self);
+}
+
+static gboolean
+font_has_color_glyphs (const PangoFont *font)
+{
+ cairo_scaled_font_t *scaled_font;
+ gboolean has_color = FALSE;
+
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
+ {
+ FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
+ has_color = (FT_HAS_COLOR (ft_face) != 0);
+ cairo_ft_scaled_font_unlock_face (scaled_font);
+ }
+
+ return has_color;
+}
+
+#define FALLBACK(...) G_STMT_START { \
+ GSK_NOTE (FALLBACK, g_print (__VA_ARGS__)); \
+ goto fallback; \
+}G_STMT_END
+
+static void
+gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ const GskVulkanPushConstants *constants,
+ GskRenderNode *node)
+{
+ GskVulkanOp op = {
+ .type = GSK_VULKAN_OP_FALLBACK,
+ .render.node = node
+ };
+ GskVulkanPipelineType pipeline_type;
+
+ switch (gsk_render_node_get_node_type (node))
+ {
+ case GSK_NOT_A_RENDER_NODE:
+ g_assert_not_reached ();
+ return;
+ case GSK_SHADOW_NODE:
+ default:
+ FALLBACK ("Unsupported node '%s'\n", node->node_class->type_name);
+
+ case GSK_REPEAT_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED;
+ else
+ FALLBACK ("Repeat nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_REPEAT;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_BLEND_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED;
+ else
+ FALLBACK ("Blend nodes can't deal with clip type %u\n", constants->clip.type);
+ 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;
+
+ case GSK_CROSS_FADE_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED;
+ else
+ FALLBACK ("Cross fade nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_CROSS_FADE;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_INSET_SHADOW_NODE:
+ if (gsk_inset_shadow_node_get_blur_radius (node) > 0)
+ FALLBACK ("Blur support not implemented for inset shadows\n");
+ else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP_ROUNDED;
+ else
+ FALLBACK ("Inset shadow nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_INSET_SHADOW;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_OUTSET_SHADOW_NODE:
+ if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
+ FALLBACK ("Blur support not implemented for outset shadows\n");
+ else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP_ROUNDED;
+ else
+ FALLBACK ("Outset shadow nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_OUTSET_SHADOW;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_CAIRO_NODE:
+ if (gsk_cairo_node_peek_surface (node) == NULL)
+ return;
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED;
+ else
+ FALLBACK ("Cairo nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_SURFACE;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_TEXT_NODE:
+ {
+ const PangoFont *font = gsk_text_node_peek_font (node);
+ const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+ guint num_glyphs = gsk_text_node_get_num_glyphs (node);
+ int i;
+ guint count;
+ guint texture_index;
+ GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
+
+ if (font_has_color_glyphs (font))
+ {
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED;
+ else
+ FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_COLOR_TEXT;
+ }
+ else
+ {
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXT;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED;
+ else
+ FALLBACK ("Text nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_TEXT;
+ }
+ op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+
+ op.text.start_glyph = 0;
+ op.text.texture_index = G_MAXUINT;
+ op.text.scale = self->scale_factor;
+
+ for (i = 0, count = 0; i < num_glyphs; i++)
+ {
+ const PangoGlyphInfo *gi = &glyphs[i];
+
+ texture_index = gsk_vulkan_renderer_cache_glyph (renderer, (PangoFont *)font, gi->glyph, op.text.scale);
+ if (op.text.texture_index == G_MAXUINT)
+ op.text.texture_index = texture_index;
+ if (texture_index != op.text.texture_index)
+ {
+ op.text.num_glyphs = count;
+
+ g_array_append_val (self->render_ops, op);
+
+ count = 1;
+ op.text.start_glyph = i;
+ op.text.texture_index = texture_index;
+ }
+ else
+ count++;
+ }
+
+ if (op.text.texture_index != G_MAXUINT && count != 0)
+ {
+ op.text.num_glyphs = count;
+ g_array_append_val (self->render_ops, op);
+ }
+
+ return;
+ }
+
+ case GSK_TEXTURE_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED;
+ else
+ FALLBACK ("Texture nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_TEXTURE;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_COLOR_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP_ROUNDED;
+ else
+ FALLBACK ("Color nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_COLOR;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_LINEAR_GRADIENT_NODE:
+ case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+ if (gsk_linear_gradient_node_get_n_color_stops (node) > GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS)
+ FALLBACK ("Linear gradient with %zu color stops, hardcoded limit is %u\n",
+ gsk_linear_gradient_node_get_n_color_stops (node),
+ GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS);
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP_ROUNDED;
+ else
+ FALLBACK ("Linear gradient nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_LINEAR_GRADIENT;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_OPACITY_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED;
+ else
+ FALLBACK ("Opacity nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_OPACITY;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_BLUR_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_BLUR;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED;
+ else
+ FALLBACK ("Blur nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_BLUR;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_COLOR_MATRIX_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED;
+ else
+ FALLBACK ("Color matrix nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_COLOR_MATRIX;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_BORDER_NODE:
+ if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
+ pipeline_type = GSK_VULKAN_PIPELINE_BORDER;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
+ pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP;
+ else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
+ pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP_ROUNDED;
+ else
+ FALLBACK ("Border nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_BORDER;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
+ g_array_append_val (self->render_ops, op);
+ return;
+
+ case GSK_CONTAINER_NODE:
+ {
+ guint i;
+
+ for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+ {
+ gsk_vulkan_render_pass_add_node (self, render, constants, gsk_container_node_get_child (node, i));
+ }
+ }
+ return;
+
+ case GSK_TRANSFORM_NODE:
+ {
+ graphene_matrix_t transform, mv;
+ GskRenderNode *child;
+
+#if 0
+ if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds))
+ FALLBACK ("Transform nodes can't deal with clip type %u\n", clip->type);
+#endif
+
+ graphene_matrix_init_from_matrix (&transform, gsk_transform_node_peek_transform (node));
+ graphene_matrix_init_from_matrix (&mv, &self->mv);
+ graphene_matrix_multiply (&transform, &mv, &self->mv);
+ child = gsk_transform_node_get_child (node);
+ if (!gsk_vulkan_push_constants_transform (&op.constants.constants, constants, &transform, &child->bounds))
+ FALLBACK ("Transform nodes can't deal with clip type %u\n", constants->clip.type);
+ op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
+ g_array_append_val (self->render_ops, op);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, child);
+ gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants);
+ graphene_matrix_init_from_matrix (&self->mv, &mv);
+ g_array_append_val (self->render_ops, op);
+ }
+ return;
+
+ case GSK_CLIP_NODE:
+ {
+ if (!gsk_vulkan_push_constants_intersect_rect (&op.constants.constants, constants, gsk_clip_node_peek_clip (node)))
+ FALLBACK ("Failed to find intersection between clip of type %u and rectangle\n", constants->clip.type);
+ if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
+ return;
+
+ op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
+ g_array_append_val (self->render_ops, op);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_clip_node_get_child (node));
+
+ gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants);
+ g_array_append_val (self->render_ops, op);
+ }
+ return;
+
+ case GSK_ROUNDED_CLIP_NODE:
+ {
+ if (!gsk_vulkan_push_constants_intersect_rounded (&op.constants.constants,
+ constants,
+ gsk_rounded_clip_node_peek_clip (node)))
+ FALLBACK ("Failed to find intersection between clip of type %u and rounded rectangle\n", constants->clip.type);
+ if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
+ return;
+
+ op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
+ g_array_append_val (self->render_ops, op);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_rounded_clip_node_get_child (node));
+
+ gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants);
+ g_array_append_val (self->render_ops, op);
+ }
+ return;
+ }
+
+ g_assert_not_reached ();
+ return;
+
+fallback:
+ switch (constants->clip.type)
+ {
+ case GSK_VULKAN_CLIP_NONE:
+ op.type = GSK_VULKAN_OP_FALLBACK;
+ break;
+ case GSK_VULKAN_CLIP_RECT:
+ op.type = GSK_VULKAN_OP_FALLBACK_CLIP;
+ gsk_rounded_rect_init_copy (&op.render.clip, &constants->clip.rect);
+ break;
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ op.type = GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP;
+ gsk_rounded_rect_init_copy (&op.render.clip, &constants->clip.rect);
+ break;
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_TEXTURE);
+ g_array_append_val (self->render_ops, op);
+}
+#undef FALLBACK
+
+void
+gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ GskRenderNode *node)
+{
+ GskVulkanOp op = { 0, };
+ graphene_matrix_t mvp;
+
+ graphene_matrix_multiply (&self->mv, &self->p, &mvp);
+ op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
+ gsk_vulkan_push_constants_init (&op.constants.constants, &mvp, &self->viewport);
+ g_array_append_val (self->render_ops, op);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, node);
+}
+
+static GskVulkanImage *
+gsk_vulkan_render_pass_get_node_as_texture (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ GskVulkanUploader *uploader,
+ GskRenderNode *node,
+ const graphene_rect_t *bounds,
+ GskVulkanClip *current_clip,
+ graphene_rect_t *tex_rect)
+{
+ GskVulkanImage *result;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ switch ((guint) gsk_render_node_get_node_type (node))
+ {
+ case GSK_TEXTURE_NODE:
+ if (graphene_rect_equal (bounds, &node->bounds))
+ {
+ result = gsk_vulkan_renderer_ref_texture_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
+ gsk_texture_node_get_texture (node),
+ uploader);
+ gsk_vulkan_render_add_cleanup_image (render, result);
+ *tex_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
+ return result;
+ }
+ break;
+
+ case GSK_CAIRO_NODE:
+ if (graphene_rect_equal (bounds, &node->bounds))
+ {
+ surface = cairo_surface_reference ((cairo_surface_t *)gsk_cairo_node_peek_surface (node));
+ goto got_surface;
+ }
+ break;
+
+ default:
+ {
+ VkSemaphore semaphore;
+ graphene_rect_t view;
+ cairo_region_t *clip;
+ GskVulkanRenderPass *pass;
+ graphene_rect_t clipped;
+
+ if (current_clip)
+ graphene_rect_intersection (&current_clip->rect.bounds, bounds, &clipped);
+ else
+ clipped = *bounds;
+
+ if (clipped.size.width == 0 || clipped.size.height == 0)
+ return NULL;
+
+ graphene_matrix_transform_bounds (&self->mv, &clipped, &view);
+ view.origin.x = floor (view.origin.x);
+ view.origin.y = floor (view.origin.y);
+ view.size.width = ceil (view.size.width);
+ view.size.height = ceil (view.size.height);
+
+ result = gsk_vulkan_image_new_for_texture (self->vulkan,
+ view.size.width,
+ view.size.height);
+
+#ifdef G_ENABLE_DEBUG
+ {
+ GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
+ gsk_profiler_counter_add (profiler,
+ self->texture_pixels,
+ view.size.width * view.size.height);
+ }
+#endif
+
+ vkCreateSemaphore (gdk_vulkan_context_get_device (self->vulkan),
+ &(VkSemaphoreCreateInfo) {
+ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ NULL,
+ 0
+ },
+ NULL,
+ &semaphore);
+
+ g_array_append_val (self->wait_semaphores, semaphore);
+
+ clip = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
+ 0, 0,
+ gsk_vulkan_image_get_width (result),
+ gsk_vulkan_image_get_height (result)
+ });
+
+ pass = gsk_vulkan_render_pass_new (self->vulkan,
+ result,
+ self->scale_factor,
+ &self->mv,
+ &view,
+ clip,
+ semaphore);
+
+ cairo_region_destroy (clip);
+
+ gsk_vulkan_render_add_render_pass (render, pass);
+ gsk_vulkan_render_pass_add (pass, render, node);
+ gsk_vulkan_render_add_cleanup_image (render, result);
+
+ /* assuming the unclipped bounds should go to texture coordinates 0..1,
+ * calculate the coordinates for the clipped texture size
+ */
+ tex_rect->origin.x = (bounds->origin.x - clipped.origin.x)/clipped.size.width;
+ tex_rect->origin.y = (bounds->origin.y - clipped.origin.y)/clipped.size.height;
+ tex_rect->size.width = bounds->size.width/clipped.size.width;
+ tex_rect->size.height = bounds->size.height/clipped.size.height;
+
+ return result;
+ }
+ }
+
+ GSK_NOTE (FALLBACK, g_print ("Node as texture not implemented for this case. Using %gx%g fallback surface\n",
+ ceil (bounds->size.width),
+ ceil (bounds->size.height)));
+#ifdef G_ENABLE_DEBUG
+ {
+ GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
+ gsk_profiler_counter_add (profiler,
+ self->fallback_pixels,
+ ceil (bounds->size.width) * ceil (bounds->size.height));
+ }
+#endif
+
+ /* XXX: We could intersect bounds with clip bounds here */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ ceil (bounds->size.width),
+ ceil (bounds->size.height));
+ cr = cairo_create (surface);
+ cairo_translate (cr, -bounds->origin.x, -bounds->origin.y);
+
+ gsk_render_node_draw (node, cr);
+
+ cairo_destroy (cr);
+
+got_surface:
+ result = gsk_vulkan_image_new_from_data (uploader,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ cairo_image_surface_get_stride (surface));
+
+ cairo_surface_destroy (surface);
+
+ gsk_vulkan_render_add_cleanup_image (render, result);
+
+ tex_rect->origin.x = (node->bounds.origin.x - bounds->origin.x)/bounds->size.width;
+ tex_rect->origin.y = (node->bounds.origin.y - bounds->origin.y)/bounds->size.height;
+ tex_rect->size.width = node->bounds.size.width/bounds->size.width;
+ tex_rect->size.height = node->bounds.size.height/bounds->size.height;
+
+ return result;
+}
+
+static void
+gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass *self,
+ GskVulkanOpRender *op,
+ GskVulkanRender *render,
+ GskVulkanUploader *uploader)
+{
+ GskRenderNode *node;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ node = op->node;
+
+ GSK_NOTE (FALLBACK,
+ g_print ("Upload op=%s, node %s[%p], bounds %gx%g\n",
+ op->type == GSK_VULKAN_OP_FALLBACK_CLIP ? "fallback-clip" :
+ (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP ? "fallback-rounded-clip" : "fallback"),
+ node->name ? node->name : node->node_class->type_name, node,
+ ceil (node->bounds.size.width),
+ ceil (node->bounds.size.height)));
+#ifdef G_ENABLE_DEBUG
+ {
+ GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
+ gsk_profiler_counter_add (profiler,
+ self->fallback_pixels,
+ ceil (node->bounds.size.width) * ceil (node->bounds.size.height));
+ }
+#endif
+
+ /* XXX: We could intersect bounds with clip bounds here */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ ceil (node->bounds.size.width * self->scale_factor),
+ ceil (node->bounds.size.height * self->scale_factor));
+ cairo_surface_set_device_scale (surface, self->scale_factor, self->scale_factor);
+ cr = cairo_create (surface);
+ cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
+
+ if (op->type == GSK_VULKAN_OP_FALLBACK_CLIP)
+ {
+ cairo_rectangle (cr,
+ op->clip.bounds.origin.x, op->clip.bounds.origin.y,
+ op->clip.bounds.size.width, op->clip.bounds.size.height);
+ cairo_clip (cr);
+ }
+ else if (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP)
+ {
+ gsk_rounded_rect_path (&op->clip, cr);
+ cairo_clip (cr);
+ }
+ else
+ {
+ g_assert (op->type == GSK_VULKAN_OP_FALLBACK);
+ }
+
+ gsk_render_node_draw (node, cr);
+
+ cairo_destroy (cr);
+
+ op->source = gsk_vulkan_image_new_from_data (uploader,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ cairo_image_surface_get_stride (surface));
+
+ op->source_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
+
+ cairo_surface_destroy (surface);
+
+ gsk_vulkan_render_add_cleanup_image (render, op->source);
+}
+
+void
+gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ GskVulkanUploader *uploader)
+{
+ GskVulkanOp *op;
+ guint i;
+ GskVulkanClip *clip = NULL;
+
+ for (i = 0; i < self->render_ops->len; i++)
+ {
+ op = &g_array_index (self->render_ops, GskVulkanOp, i);
+
+ switch (op->type)
+ {
+ case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
+ gsk_vulkan_render_pass_upload_fallback (self, &op->render, render, uploader);
+ break;
+
+ case GSK_VULKAN_OP_SURFACE:
+ {
+ cairo_surface_t *surface;
+
+ surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (op->render.node);
+ op->render.source = gsk_vulkan_image_new_from_data (uploader,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ cairo_image_surface_get_stride (surface));
+ op->render.source_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
+
+ gsk_vulkan_render_add_cleanup_image (render, op->render.source);
+ }
+ break;
+
+ case GSK_VULKAN_OP_TEXT:
+ case GSK_VULKAN_OP_COLOR_TEXT:
+ {
+ op->text.source = gsk_vulkan_renderer_ref_glyph_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
+ uploader,
+ op->text.texture_index);
+ gsk_vulkan_render_add_cleanup_image (render, op->text.source);
+ }
+ break;
+
+ case GSK_VULKAN_OP_TEXTURE:
+ {
+ op->render.source = gsk_vulkan_renderer_ref_texture_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
+ gsk_texture_node_get_texture (op->render.node),
+ uploader);
+ op->render.source_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
+ gsk_vulkan_render_add_cleanup_image (render, op->render.source);
+ }
+ break;
+
+ case GSK_VULKAN_OP_OPACITY:
+ {
+ GskRenderNode *child = gsk_opacity_node_get_child (op->render.node);
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ child,
+ &child->bounds,
+ clip,
+ &op->render.source_rect);
+ }
+ break;
+
+ case GSK_VULKAN_OP_REPEAT:
+ {
+ GskRenderNode *child = gsk_repeat_node_get_child (op->render.node);
+ const graphene_rect_t *bounds = &op->render.node->bounds;
+ const graphene_rect_t *child_bounds = gsk_repeat_node_peek_child_bounds (op->render.node);
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ child,
+ child_bounds,
+ NULL,
+ &op->render.source_rect);
+
+ op->render.source_rect.origin.x = (bounds->origin.x - child_bounds->origin.x)/child_bounds->size.width;
+ op->render.source_rect.origin.y = (bounds->origin.y - child_bounds->origin.y)/child_bounds->size.height;
+ op->render.source_rect.size.width = bounds->size.width / child_bounds->size.width;
+ op->render.source_rect.size.height = bounds->size.height / child_bounds->size.height;
+ }
+ break;
+
+ case GSK_VULKAN_OP_BLUR:
+ {
+ GskRenderNode *child = gsk_blur_node_get_child (op->render.node);
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ child,
+ &child->bounds,
+ clip,
+ &op->render.source_rect);
+ }
+ break;
+
+ case GSK_VULKAN_OP_COLOR_MATRIX:
+ {
+ GskRenderNode *child = gsk_color_matrix_node_get_child (op->render.node);
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ child,
+ &child->bounds,
+ clip,
+ &op->render.source_rect);
+ }
+ break;
+
+ case GSK_VULKAN_OP_CROSS_FADE:
+ {
+ GskRenderNode *start = gsk_cross_fade_node_get_start_child (op->render.node);
+ GskRenderNode *end = gsk_cross_fade_node_get_end_child (op->render.node);
+ const graphene_rect_t *bounds = &op->render.node->bounds;
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ start,
+ &start->bounds,
+ clip,
+ &op->render.source_rect);
+ op->render.source_rect.origin.x = (bounds->origin.x - start->bounds.origin.x)/start->bounds.size.width;
+ op->render.source_rect.origin.y = (bounds->origin.y - start->bounds.origin.y)/start->bounds.size.height;
+ op->render.source_rect.size.width = bounds->size.width / start->bounds.size.width;
+ op->render.source_rect.size.height = bounds->size.height / start->bounds.size.height;
+
+ op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ end,
+ &end->bounds,
+ clip,
+ &op->render.source2_rect);
+ op->render.source2_rect.origin.x = (bounds->origin.x - end->bounds.origin.x)/end->bounds.size.width;
+ op->render.source2_rect.origin.y = (bounds->origin.y - end->bounds.origin.y)/end->bounds.size.height;
+ op->render.source2_rect.size.width = bounds->size.width / end->bounds.size.width;
+ op->render.source2_rect.size.height = bounds->size.height / end->bounds.size.height;
+ }
+ break;
+
+ case GSK_VULKAN_OP_BLEND_MODE:
+ {
+ GskRenderNode *top = gsk_blend_node_get_top_child (op->render.node);
+ GskRenderNode *bottom = gsk_blend_node_get_bottom_child (op->render.node);
+ const graphene_rect_t *bounds = &op->render.node->bounds;
+
+ op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ top,
+ &top->bounds,
+ clip,
+ &op->render.source_rect);
+ op->render.source_rect.origin.x = (bounds->origin.x - top->bounds.origin.x)/top->bounds.size.width;
+ op->render.source_rect.origin.y = (bounds->origin.y - top->bounds.origin.y)/top->bounds.size.height;
+ op->render.source_rect.size.width = bounds->size.width / top->bounds.size.width;
+ op->render.source_rect.size.height = bounds->size.height / top->bounds.size.height;
+
+ op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
+ render,
+ uploader,
+ bottom,
+ &bottom->bounds,
+ clip,
+ &op->render.source2_rect);
+ op->render.source2_rect.origin.x = (bounds->origin.x - bottom->bounds.origin.x)/bottom->bounds.size.width;
+ op->render.source2_rect.origin.y = (bounds->origin.y - bottom->bounds.origin.y)/bottom->bounds.size.height;
+ op->render.source2_rect.size.width = bounds->size.width / bottom->bounds.size.width;
+ op->render.source2_rect.size.height = bounds->size.height / bottom->bounds.size.height;
+ }
+ break;
+
+ case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
+ clip = &op->constants.constants.clip;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ case GSK_VULKAN_OP_COLOR:
+ case GSK_VULKAN_OP_LINEAR_GRADIENT:
+ case GSK_VULKAN_OP_BORDER:
+ case GSK_VULKAN_OP_INSET_SHADOW:
+ case GSK_VULKAN_OP_OUTSET_SHADOW:
+ break;
+ }
+ }
+}
+
+static gsize
+gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
+{
+ GskVulkanOp *op;
+ gsize n_bytes;
+ guint i;
+
+ n_bytes = 0;
+ for (i = 0; i < self->render_ops->len; i++)
+ {
+ op = &g_array_index (self->render_ops, GskVulkanOp, i);
+
+ switch (op->type)
+ {
+ case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
+ case GSK_VULKAN_OP_SURFACE:
+ case GSK_VULKAN_OP_TEXTURE:
+ case GSK_VULKAN_OP_REPEAT:
+ op->render.vertex_count = gsk_vulkan_texture_pipeline_count_vertex_data (GSK_VULKAN_TEXTURE_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_TEXT:
+ op->text.vertex_count = gsk_vulkan_text_pipeline_count_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
+ op->text.num_glyphs);
+ n_bytes += op->text.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_COLOR_TEXT:
+ op->text.vertex_count = gsk_vulkan_color_text_pipeline_count_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->render.pipeline),
+ op->text.num_glyphs);
+ n_bytes += op->text.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_COLOR:
+ op->render.vertex_count = gsk_vulkan_color_pipeline_count_vertex_data (GSK_VULKAN_COLOR_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_LINEAR_GRADIENT:
+ op->render.vertex_count = gsk_vulkan_linear_gradient_pipeline_count_vertex_data (GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_OPACITY:
+ case GSK_VULKAN_OP_COLOR_MATRIX:
+ op->render.vertex_count = gsk_vulkan_effect_pipeline_count_vertex_data (GSK_VULKAN_EFFECT_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_BLUR:
+ op->render.vertex_count = gsk_vulkan_blur_pipeline_count_vertex_data (GSK_VULKAN_BLUR_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_BORDER:
+ op->render.vertex_count = gsk_vulkan_border_pipeline_count_vertex_data (GSK_VULKAN_BORDER_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_INSET_SHADOW:
+ case GSK_VULKAN_OP_OUTSET_SHADOW:
+ op->render.vertex_count = gsk_vulkan_box_shadow_pipeline_count_vertex_data (GSK_VULKAN_BOX_SHADOW_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_CROSS_FADE:
+ op->render.vertex_count = gsk_vulkan_cross_fade_pipeline_count_vertex_data (GSK_VULKAN_CROSS_FADE_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ case GSK_VULKAN_OP_BLEND_MODE:
+ op->render.vertex_count = gsk_vulkan_blend_mode_pipeline_count_vertex_data (GSK_VULKAN_BLEND_MODE_PIPELINE (op->render.pipeline));
+ n_bytes += op->render.vertex_count;
+ break;
+
+ default:
+ g_assert_not_reached ();
+
+ case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
+ continue;
+ }
+ }
+
+ return n_bytes;
+}
+
+static gsize
+gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ guchar *data,
+ gsize offset,
+ gsize total)
+{
+ GskVulkanOp *op;
+ gsize n_bytes;
+ guint i;
+
+ n_bytes = 0;
+ for (i = 0; i < self->render_ops->len; i++)
+ {
+ op = &g_array_index (self->render_ops, GskVulkanOp, i);
+
+ switch (op->type)
+ {
+ case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
+ case GSK_VULKAN_OP_SURFACE:
+ case GSK_VULKAN_OP_TEXTURE:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_texture_pipeline_collect_vertex_data (GSK_VULKAN_TEXTURE_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect);
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_REPEAT:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_texture_pipeline_collect_vertex_data (GSK_VULKAN_TEXTURE_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect);
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_TEXT:
+ {
+ op->text.vertex_offset = offset + n_bytes;
+ gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
+ data + n_bytes + offset,
+ GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
+ &op->text.node->bounds,
+ (PangoFont *)gsk_text_node_peek_font (op->text.node),
+ gsk_text_node_get_num_glyphs (op->text.node),
+ gsk_text_node_peek_glyphs (op->text.node),
+ gsk_text_node_peek_color (op->text.node),
+ gsk_text_node_get_x (op->text.node),
+ gsk_text_node_get_y (op->text.node),
+ op->text.start_glyph,
+ op->text.num_glyphs,
+ op->text.scale);
+ n_bytes += op->text.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_COLOR_TEXT:
+ {
+ op->text.vertex_offset = offset + n_bytes;
+ gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->text.pipeline),
+ data + n_bytes + offset,
+ GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
+ &op->text.node->bounds,
+ (PangoFont *)gsk_text_node_peek_font (op->text.node),
+ gsk_text_node_get_num_glyphs (op->text.node),
+ gsk_text_node_peek_glyphs (op->text.node),
+ gsk_text_node_get_x (op->text.node),
+ gsk_text_node_get_y (op->text.node),
+ op->text.start_glyph,
+ op->text.num_glyphs,
+ op->text.scale);
+ n_bytes += op->text.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_COLOR:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_color_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ gsk_color_node_peek_color (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_LINEAR_GRADIENT:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ gsk_linear_gradient_node_peek_start (op->render.node),
+ gsk_linear_gradient_node_peek_end (op->render.node),
+ gsk_render_node_get_node_type (op->render.node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
+ gsk_linear_gradient_node_get_n_color_stops (op->render.node),
+ gsk_linear_gradient_node_peek_color_stops (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_OPACITY:
+ {
+ graphene_matrix_t color_matrix;
+ graphene_vec4_t color_offset;
+
+ graphene_matrix_init_from_float (&color_matrix,
+ (float[16]) {
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, gsk_opacity_node_get_opacity (op->render.node)
+ });
+ graphene_vec4_init (&color_offset, 0.0, 0.0, 0.0, 0.0);
+ op->render.vertex_offset = offset + n_bytes;
+
+ gsk_vulkan_effect_pipeline_collect_vertex_data (GSK_VULKAN_EFFECT_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect,
+ &color_matrix,
+ &color_offset);
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_BLUR:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_blur_pipeline_collect_vertex_data (GSK_VULKAN_BLUR_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect,
+ gsk_blur_node_get_radius (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_COLOR_MATRIX:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_effect_pipeline_collect_vertex_data (GSK_VULKAN_EFFECT_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect,
+ gsk_color_matrix_node_peek_color_matrix (op->render.node),
+ gsk_color_matrix_node_peek_color_offset (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_BORDER:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_border_pipeline_collect_vertex_data (GSK_VULKAN_BORDER_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ gsk_border_node_peek_outline (op->render.node),
+ gsk_border_node_peek_widths (op->render.node),
+ gsk_border_node_peek_colors (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_INSET_SHADOW:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GSK_VULKAN_BOX_SHADOW_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ gsk_inset_shadow_node_peek_outline (op->render.node),
+ gsk_inset_shadow_node_peek_color (op->render.node),
+ gsk_inset_shadow_node_get_dx (op->render.node),
+ gsk_inset_shadow_node_get_dy (op->render.node),
+ gsk_inset_shadow_node_get_spread (op->render.node),
+ gsk_inset_shadow_node_get_blur_radius (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_OUTSET_SHADOW:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GSK_VULKAN_BOX_SHADOW_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ gsk_outset_shadow_node_peek_outline (op->render.node),
+ gsk_outset_shadow_node_peek_color (op->render.node),
+ gsk_outset_shadow_node_get_dx (op->render.node),
+ gsk_outset_shadow_node_get_dy (op->render.node),
+ gsk_outset_shadow_node_get_spread (op->render.node),
+ gsk_outset_shadow_node_get_blur_radius (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_CROSS_FADE:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GSK_VULKAN_CROSS_FADE_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect,
+ &op->render.source2_rect,
+ gsk_cross_fade_node_get_progress (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ case GSK_VULKAN_OP_BLEND_MODE:
+ {
+ op->render.vertex_offset = offset + n_bytes;
+ gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GSK_VULKAN_BLEND_MODE_PIPELINE (op->render.pipeline),
+ data + n_bytes + offset,
+ &op->render.node->bounds,
+ &op->render.source_rect,
+ &op->render.source2_rect,
+ gsk_blend_node_get_blend_mode (op->render.node));
+ n_bytes += op->render.vertex_count;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
+ continue;
+ }
+
+ g_assert (n_bytes + offset <= total);
+ }
+
+ return n_bytes;
+}
+
+static GskVulkanBuffer *
+gsk_vulkan_render_pass_get_vertex_data (GskVulkanRenderPass *self,
+ GskVulkanRender *render)
+{
+ if (self->vertex_data == NULL)
+ {
+ gsize n_bytes;
+ guchar *data;
+
+ n_bytes = gsk_vulkan_render_pass_count_vertex_data (self);
+ self->vertex_data = gsk_vulkan_buffer_new (self->vulkan, n_bytes);
+ data = gsk_vulkan_buffer_map (self->vertex_data);
+ gsk_vulkan_render_pass_collect_vertex_data (self, render, data, 0, n_bytes);
+ gsk_vulkan_buffer_unmap (self->vertex_data);
+ }
+
+ return self->vertex_data;
+}
+
+gsize
+gsk_vulkan_render_pass_get_wait_semaphores (GskVulkanRenderPass *self,
+ VkSemaphore **semaphores)
+{
+ *semaphores = (VkSemaphore *)self->wait_semaphores->data;
+ return self->wait_semaphores->len;
+}
+
+gsize
+gsk_vulkan_render_pass_get_signal_semaphores (GskVulkanRenderPass *self,
+ VkSemaphore **semaphores)
+{
+ *semaphores = (VkSemaphore *)&self->signal_semaphore;
+ return self->signal_semaphore != VK_NULL_HANDLE ? 1 : 0;
+}
+
+void
+gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
+ GskVulkanRender *render)
+{
+ GskVulkanOp *op;
+ guint i;
+
+ for (i = 0; i < self->render_ops->len; i++)
+ {
+ op = &g_array_index (self->render_ops, GskVulkanOp, i);
+
+ switch (op->type)
+ {
+ case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
+ case GSK_VULKAN_OP_SURFACE:
+ case GSK_VULKAN_OP_TEXTURE:
+ case GSK_VULKAN_OP_OPACITY:
+ case GSK_VULKAN_OP_BLUR:
+ case GSK_VULKAN_OP_COLOR_MATRIX:
+ if (op->render.source)
+ op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, FALSE);
+ break;
+
+ case GSK_VULKAN_OP_REPEAT:
+ if (op->render.source)
+ op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, TRUE);
+ 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);
+ break;
+
+ case GSK_VULKAN_OP_CROSS_FADE:
+ case GSK_VULKAN_OP_BLEND_MODE:
+ if (op->render.source && op->render.source2)
+ {
+ op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, FALSE);
+ op->render.descriptor_set_index2 = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source2, FALSE);
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+
+ case GSK_VULKAN_OP_COLOR:
+ case GSK_VULKAN_OP_LINEAR_GRADIENT:
+ case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
+ case GSK_VULKAN_OP_BORDER:
+ case GSK_VULKAN_OP_INSET_SHADOW:
+ case GSK_VULKAN_OP_OUTSET_SHADOW:
+ break;
+ }
+ }
+}
+
+static void
+gsk_vulkan_render_pass_draw_rect (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ guint layout_count,
+ VkPipelineLayout *pipeline_layout,
+ VkCommandBuffer command_buffer)
+{
+ GskVulkanPipeline *current_pipeline = NULL;
+ gsize current_draw_index = 0;
+ GskVulkanOp *op;
+ guint i, step;
+ GskVulkanBuffer *vertex_buffer;
+
+ vertex_buffer = gsk_vulkan_render_pass_get_vertex_data (self, render);
+
+ for (i = 0; i < self->render_ops->len; i += step)
+ {
+ op = &g_array_index (self->render_ops, GskVulkanOp, i);
+ step = 1;
+
+ switch (op->type)
+ {
+ case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
+ case GSK_VULKAN_OP_SURFACE:
+ case GSK_VULKAN_OP_TEXTURE:
+ case GSK_VULKAN_OP_REPEAT:
+ if (!op->render.source)
+ continue;
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_texture_pipeline_draw (GSK_VULKAN_TEXTURE_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_TEXT:
+ if (current_pipeline != op->text.pipeline)
+ {
+ current_pipeline = op->text.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->text.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_text_pipeline_draw (GSK_VULKAN_TEXT_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, op->text.num_glyphs);
+ break;
+
+ case GSK_VULKAN_OP_COLOR_TEXT:
+ if (current_pipeline != op->text.pipeline)
+ {
+ current_pipeline = op->text.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->text.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_color_text_pipeline_draw (GSK_VULKAN_COLOR_TEXT_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, op->text.num_glyphs);
+ break;
+
+ case GSK_VULKAN_OP_OPACITY:
+ case GSK_VULKAN_OP_COLOR_MATRIX:
+ if (!op->render.source)
+ continue;
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_effect_pipeline_draw (GSK_VULKAN_EFFECT_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_BLUR:
+ if (!op->render.source)
+ continue;
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 1,
+ (VkDescriptorSet[1]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_blur_pipeline_draw (GSK_VULKAN_BLUR_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_COLOR:
+ 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;
+ }
+
+ for (step = 1; step + i < self->render_ops->len; step++)
+ {
+ GskVulkanOp *cmp = &g_array_index (self->render_ops, GskVulkanOp, i + step);
+ if (cmp->type != GSK_VULKAN_OP_COLOR ||
+ cmp->render.pipeline != current_pipeline)
+ break;
+ }
+ current_draw_index += gsk_vulkan_color_pipeline_draw (GSK_VULKAN_COLOR_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, step);
+ break;
+
+ case GSK_VULKAN_OP_LINEAR_GRADIENT:
+ 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;
+ }
+ current_draw_index += gsk_vulkan_linear_gradient_pipeline_draw (GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_BORDER:
+ 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;
+ }
+ current_draw_index += gsk_vulkan_border_pipeline_draw (GSK_VULKAN_BORDER_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_INSET_SHADOW:
+ case GSK_VULKAN_OP_OUTSET_SHADOW:
+ 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;
+ }
+ current_draw_index += gsk_vulkan_box_shadow_pipeline_draw (GSK_VULKAN_BOX_SHADOW_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
+ for (int i = 0; i < layout_count; i++)
+ gsk_vulkan_push_constants_push (&op->constants.constants,
+ command_buffer,
+ pipeline_layout[i]);
+ break;
+
+ case GSK_VULKAN_OP_CROSS_FADE:
+ if (!op->render.source || !op->render.source2)
+ continue;
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 2,
+ (VkDescriptorSet[2]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index),
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_cross_fade_pipeline_draw (GSK_VULKAN_CROSS_FADE_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ case GSK_VULKAN_OP_BLEND_MODE:
+ if (!op->render.source || !op->render.source2)
+ continue;
+ if (current_pipeline != op->render.pipeline)
+ {
+ current_pipeline = op->render.pipeline;
+ vkCmdBindPipeline (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline (current_pipeline));
+ vkCmdBindVertexBuffers (command_buffer,
+ 0,
+ 1,
+ (VkBuffer[1]) {
+ gsk_vulkan_buffer_get_buffer (vertex_buffer)
+ },
+ (VkDeviceSize[1]) { op->render.vertex_offset });
+ current_draw_index = 0;
+ }
+
+ vkCmdBindDescriptorSets (command_buffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
+ 0,
+ 2,
+ (VkDescriptorSet[2]) {
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index),
+ gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2)
+ },
+ 0,
+ NULL);
+
+ current_draw_index += gsk_vulkan_blend_mode_pipeline_draw (GSK_VULKAN_BLEND_MODE_PIPELINE (current_pipeline),
+ command_buffer,
+ current_draw_index, 1);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+}
+
+void
+gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ guint layout_count,
+ VkPipelineLayout *pipeline_layout,
+ VkCommandBuffer command_buffer)
+{
+ guint i;
+
+ vkCmdSetViewport (command_buffer,
+ 0,
+ 1,
+ &(VkViewport) {
+ .x = 0,
+ .y = 0,
+ .width = self->viewport.size.width,
+ .height = self->viewport.size.height,
+ .minDepth = 0,
+ .maxDepth = 1
+ });
+
+ for (i = 0; i < cairo_region_num_rectangles (self->clip); i++)
+ {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (self->clip, i, &rect);
+
+ vkCmdSetScissor (command_buffer,
+ 0,
+ 1,
+ &(VkRect2D) {
+ { rect.x * self->scale_factor, rect.y * self->scale_factor },
+ { rect.width * self->scale_factor, rect.height * self->scale_factor }
+ });
+
+ vkCmdBeginRenderPass (command_buffer,
+ &(VkRenderPassBeginInfo) {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .renderPass = self->render_pass,
+ .framebuffer = gsk_vulkan_render_get_framebuffer (render, self->target),
+ .renderArea = {
+ { rect.x * self->scale_factor, rect.y * self->scale_factor },
+ { rect.width * self->scale_factor, rect.height * self->scale_factor }
+ },
+ .clearValueCount = 1,
+ .pClearValues = (VkClearValue [1]) {
+ { .color = { .float32 = { 0.f, 0.f, 0.f, 0.f } } }
+ }
+ },
+ VK_SUBPASS_CONTENTS_INLINE);
+
+ gsk_vulkan_render_pass_draw_rect (self, render, layout_count, pipeline_layout, command_buffer);
+
+ vkCmdEndRenderPass (command_buffer);
+ }
+}
diff --git a/gsk/vulkan/gskvulkanrenderpassprivate.h b/gsk/vulkan/gskvulkanrenderpassprivate.h
new file mode 100644
index 0000000000..379db3bac2
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrenderpassprivate.h
@@ -0,0 +1,45 @@
+#ifndef __GSK_VULKAN_RENDER_PASS_PRIVATE_H__
+#define __GSK_VULKAN_RENDER_PASS_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <gsk/gskrendernode.h>
+
+#include "gskvulkanbufferprivate.h"
+#include "gskvulkanrenderprivate.h"
+#include "gsk/gskprivate.h"
+
+G_BEGIN_DECLS
+
+
+GskVulkanRenderPass * gsk_vulkan_render_pass_new (GdkVulkanContext *context,
+ GskVulkanImage *target,
+ int scale_factor,
+ graphene_matrix_t *mv,
+ graphene_rect_t *viewport,
+ cairo_region_t *clip,
+ VkSemaphore signal_semaphore);
+
+void gsk_vulkan_render_pass_free (GskVulkanRenderPass *self);
+
+void gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ GskRenderNode *node);
+
+void gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ GskVulkanUploader *uploader);
+void gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
+ GskVulkanRender *render);
+void gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
+ GskVulkanRender *render,
+ guint layout_count,
+ VkPipelineLayout *pipeline_layout,
+ VkCommandBuffer command_buffer);
+gsize gsk_vulkan_render_pass_get_wait_semaphores (GskVulkanRenderPass *self,
+ VkSemaphore **semaphores);
+gsize gsk_vulkan_render_pass_get_signal_semaphores (GskVulkanRenderPass *self,
+ VkSemaphore **semaphores);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_RENDER_PASS_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanrenderprivate.h b/gsk/vulkan/gskvulkanrenderprivate.h
new file mode 100644
index 0000000000..45cfa38b59
--- /dev/null
+++ b/gsk/vulkan/gskvulkanrenderprivate.h
@@ -0,0 +1,94 @@
+#ifndef __GSK_VULKAN_RENDER_PRIVATE_H__
+#define __GSK_VULKAN_RENDER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <gsk/gskrendernode.h>
+
+#include "gskvulkanimageprivate.h"
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrenderpassprivate.h"
+#include "gsk/gskprivate.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSK_VULKAN_PIPELINE_TEXTURE,
+ GSK_VULKAN_PIPELINE_TEXTURE_CLIP,
+ GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_COLOR,
+ GSK_VULKAN_PIPELINE_COLOR_CLIP,
+ GSK_VULKAN_PIPELINE_COLOR_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_LINEAR_GRADIENT,
+ GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP,
+ GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_COLOR_MATRIX,
+ GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP,
+ GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_BORDER,
+ GSK_VULKAN_PIPELINE_BORDER_CLIP,
+ GSK_VULKAN_PIPELINE_BORDER_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_INSET_SHADOW,
+ GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP,
+ GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_OUTSET_SHADOW,
+ GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP,
+ GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_BLUR,
+ GSK_VULKAN_PIPELINE_BLUR_CLIP,
+ GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_TEXT,
+ GSK_VULKAN_PIPELINE_TEXT_CLIP,
+ GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP,
+ GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_CROSS_FADE,
+ GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP,
+ GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED,
+ GSK_VULKAN_PIPELINE_BLEND_MODE,
+ GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP,
+ GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED,
+ /* add more */
+ GSK_VULKAN_N_PIPELINES
+} GskVulkanPipelineType;
+
+GskVulkanRender * gsk_vulkan_render_new (GskRenderer *renderer,
+ GdkVulkanContext *context);
+void gsk_vulkan_render_free (GskVulkanRender *self);
+
+gboolean gsk_vulkan_render_is_busy (GskVulkanRender *self);
+void gsk_vulkan_render_reset (GskVulkanRender *self,
+ GskVulkanImage *target,
+ const graphene_rect_t *rect);
+
+GskRenderer * gsk_vulkan_render_get_renderer (GskVulkanRender *self);
+
+void gsk_vulkan_render_add_cleanup_image (GskVulkanRender *self,
+ GskVulkanImage *image);
+
+void gsk_vulkan_render_add_node (GskVulkanRender *self,
+ GskRenderNode *node);
+
+void gsk_vulkan_render_add_render_pass (GskVulkanRender *self,
+ GskVulkanRenderPass *pass);
+
+void gsk_vulkan_render_upload (GskVulkanRender *self);
+
+GskVulkanPipeline * gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
+ GskVulkanPipelineType pipeline_type);
+VkDescriptorSet gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self,
+ gsize id);
+gsize gsk_vulkan_render_reserve_descriptor_set (GskVulkanRender *self,
+ GskVulkanImage *source,
+ gboolean repeat);
+void gsk_vulkan_render_draw (GskVulkanRender *self);
+
+void gsk_vulkan_render_submit (GskVulkanRender *self);
+
+GdkTexture * gsk_vulkan_render_download_target (GskVulkanRender *self);
+VkFramebuffer gsk_vulkan_render_get_framebuffer (GskVulkanRender *self,
+ GskVulkanImage *image);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_RENDER_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkanshader.c b/gsk/vulkan/gskvulkanshader.c
new file mode 100644
index 0000000000..1010b21304
--- /dev/null
+++ b/gsk/vulkan/gskvulkanshader.c
@@ -0,0 +1,102 @@
+#include "config.h"
+
+#include "gskvulkanshaderprivate.h"
+#include "gskvulkanpipelineprivate.h"
+
+struct _GskVulkanShader
+{
+ GdkVulkanContext *vulkan;
+
+ GskVulkanShaderType type;
+ VkShaderModule vk_shader;
+};
+
+static GskVulkanShader *
+gsk_vulkan_shader_new_from_bytes (GdkVulkanContext *context,
+ GskVulkanShaderType type,
+ GBytes *bytes,
+ GError **error)
+{
+ GskVulkanShader *self;
+ VkShaderModule shader;
+ VkResult res;
+
+ res = GSK_VK_CHECK (vkCreateShaderModule, gdk_vulkan_context_get_device (context),
+ &(VkShaderModuleCreateInfo) {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .codeSize = g_bytes_get_size (bytes),
+ .pCode = (uint32_t *) g_bytes_get_data (bytes, NULL),
+ },
+ NULL,
+ &shader);
+ if (res != VK_SUCCESS)
+ {
+ /* Someone invent better error categories plz */
+ g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
+ "Could not create shader: %s", gdk_vulkan_strerror (res));
+ return NULL;
+ }
+
+ self = g_slice_new0 (GskVulkanShader);
+
+ self->vulkan = g_object_ref (context);
+ self->type = type;
+ self->vk_shader = shader;
+
+ return self;
+}
+
+GskVulkanShader *
+gsk_vulkan_shader_new_from_resource (GdkVulkanContext *context,
+ GskVulkanShaderType type,
+ const char *resource_name,
+ GError **error)
+{
+ GskVulkanShader *self;
+ GBytes *bytes;
+ GError *local_error = NULL;
+ char *path;
+
+ path = g_strconcat ("/org/gtk/libgsk/vulkan/",
+ resource_name,
+ type == GSK_VULKAN_SHADER_VERTEX ? ".vert.spv" : ".frag.spv",
+ NULL);
+ bytes = g_resources_lookup_data (path, 0, &local_error);
+ g_free (path);
+ if (bytes == NULL)
+ {
+ GSK_NOTE (VULKAN, g_printerr ("Error loading shader data: %s\n", local_error->message));
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+
+ self = gsk_vulkan_shader_new_from_bytes (context, type, bytes, error);
+ g_bytes_unref (bytes);
+
+ return self;
+}
+
+void
+gsk_vulkan_shader_free (GskVulkanShader *self)
+{
+ vkDestroyShaderModule (gdk_vulkan_context_get_device (self->vulkan),
+ self->vk_shader,
+ NULL);
+
+ g_object_unref (self->vulkan);
+
+ g_slice_free (GskVulkanShader, self);
+}
+
+GskVulkanShaderType
+gsk_vulkan_shader_get_type (GskVulkanShader *shader)
+{
+ return shader->type;
+}
+
+VkShaderModule
+gsk_vulkan_shader_get_module (GskVulkanShader *shader)
+{
+ return shader->vk_shader;
+}
+
diff --git a/gsk/vulkan/gskvulkanshaderprivate.h b/gsk/vulkan/gskvulkanshaderprivate.h
new file mode 100644
index 0000000000..e94424f30a
--- /dev/null
+++ b/gsk/vulkan/gskvulkanshaderprivate.h
@@ -0,0 +1,34 @@
+#ifndef __GSK_VULKAN_SHADER_PRIVATE_H__
+#define __GSK_VULKAN_SHADER_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSK_VULKAN_SHADER_VERTEX,
+ GSK_VULKAN_SHADER_FRAGMENT
+} GskVulkanShaderType;
+
+typedef struct _GskVulkanShader GskVulkanShader;
+
+#define GST_VULKAN_SHADER_STAGE_CREATE_INFO(shader) \
+ (VkPipelineShaderStageCreateInfo) { \
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, \
+ .stage = gsk_vulkan_shader_get_type (shader) == GSK_VULKAN_SHADER_VERTEX ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, \
+ .module = gsk_vulkan_shader_get_module (shader), \
+ .pName = "main", \
+}
+
+GskVulkanShader * gsk_vulkan_shader_new_from_resource (GdkVulkanContext *context,
+ GskVulkanShaderType type,
+ const char *resource_name,
+ GError **error);
+void gsk_vulkan_shader_free (GskVulkanShader *shader);
+
+GskVulkanShaderType gsk_vulkan_shader_get_type (GskVulkanShader *shader);
+VkShaderModule gsk_vulkan_shader_get_module (GskVulkanShader *shader);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_SHADER_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkantextpipeline.c b/gsk/vulkan/gskvulkantextpipeline.c
new file mode 100644
index 0000000000..361c53675d
--- /dev/null
+++ b/gsk/vulkan/gskvulkantextpipeline.c
@@ -0,0 +1,170 @@
+#include "config.h"
+
+#include "gskvulkantextpipelineprivate.h"
+
+struct _GskVulkanTextPipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanTextInstance GskVulkanTextInstance;
+
+struct _GskVulkanTextInstance
+{
+ float rect[4];
+ float tex_rect[4];
+ float color[4];
+};
+
+G_DEFINE_TYPE (GskVulkanTextPipeline, gsk_vulkan_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanTextInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, tex_rect),
+ },
+ {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextInstance, color),
+ }
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_text_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanTextPipeline *self = GSK_VULKAN_TEXT_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_text_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_text_pipeline_class_init (GskVulkanTextPipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_text_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_text_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_text_pipeline_init (GskVulkanTextPipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_text_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new_full (GSK_TYPE_VULKAN_TEXT_PIPELINE, context, layout, shader_name, render_pass,
+ VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
+}
+
+gsize
+gsk_vulkan_text_pipeline_count_vertex_data (GskVulkanTextPipeline *pipeline,
+ int num_instances)
+{
+ return sizeof (GskVulkanTextInstance) * num_instances;
+}
+
+void
+gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ guint total_glyphs,
+ const PangoGlyphInfo *glyphs,
+ const GdkRGBA *color,
+ float x,
+ float y,
+ guint start_glyph,
+ guint num_glyphs,
+ float scale)
+{
+ GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
+ int i;
+ int count = 0;
+ int x_position = 0;
+
+ for (i = 0; i < start_glyph; i++)
+ x_position += glyphs[i].geometry.width;
+
+ for (; i < total_glyphs && count < num_glyphs; i++)
+ {
+ const PangoGlyphInfo *gi = &glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY)
+ {
+ double cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ double cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
+ GskVulkanTextInstance *instance = &instances[count];
+ GskVulkanCachedGlyph *glyph;
+
+ glyph = gsk_vulkan_renderer_get_cached_glyph (renderer, font, gi->glyph, scale);
+
+ instance->tex_rect[0] = glyph->tx;
+ instance->tex_rect[1] = glyph->ty;
+ instance->tex_rect[2] = glyph->tw;
+ instance->tex_rect[3] = glyph->th;
+
+ instance->rect[0] = x + cx + glyph->draw_x;
+ instance->rect[1] = y + cy + glyph->draw_y;
+ instance->rect[2] = glyph->draw_width;
+ instance->rect[3] = glyph->draw_height;
+
+ instance->color[0] = color->red;
+ instance->color[1] = color->green;
+ instance->color[2] = color->blue;
+ instance->color[3] = color->alpha;
+
+ count++;
+ }
+ x_position += gi->geometry.width;
+ }
+}
+
+gsize
+gsk_vulkan_text_pipeline_draw (GskVulkanTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkantextpipelineprivate.h b/gsk/vulkan/gskvulkantextpipelineprivate.h
new file mode 100644
index 0000000000..47517de03c
--- /dev/null
+++ b/gsk/vulkan/gskvulkantextpipelineprivate.h
@@ -0,0 +1,44 @@
+#ifndef __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+#include "gskvulkanrendererprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanTextPipelineLayout GskVulkanTextPipelineLayout;
+
+#define GSK_TYPE_VULKAN_TEXT_PIPELINE (gsk_vulkan_text_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanTextPipeline, gsk_vulkan_text_pipeline, GSK, VULKAN_TEXT_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_text_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_text_pipeline_count_vertex_data (GskVulkanTextPipeline *pipeline,
+ int num_instances);
+void gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline *pipeline,
+ guchar *data,
+ GskVulkanRenderer *renderer,
+ const graphene_rect_t *rect,
+ PangoFont *font,
+ guint total_glyphs,
+ const PangoGlyphInfo *glyphs,
+ const GdkRGBA *color,
+ float x,
+ float y,
+ guint start_glyph,
+ guint num_glyphs,
+ float scale);
+gsize gsk_vulkan_text_pipeline_draw (GskVulkanTextPipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_TEXT_PIPELINE_PRIVATE_H__ */
diff --git a/gsk/vulkan/gskvulkantexturepipeline.c b/gsk/vulkan/gskvulkantexturepipeline.c
new file mode 100644
index 0000000000..9db719093b
--- /dev/null
+++ b/gsk/vulkan/gskvulkantexturepipeline.c
@@ -0,0 +1,122 @@
+#include "config.h"
+
+#include "gskvulkantexturepipelineprivate.h"
+
+struct _GskVulkanTexturePipeline
+{
+ GObject parent_instance;
+};
+
+typedef struct _GskVulkanTextureInstance GskVulkanTextureInstance;
+
+struct _GskVulkanTextureInstance
+{
+ float rect[4];
+ float tex_rect[4];
+};
+
+G_DEFINE_TYPE (GskVulkanTexturePipeline, gsk_vulkan_texture_pipeline, GSK_TYPE_VULKAN_PIPELINE)
+
+static const VkPipelineVertexInputStateCreateInfo *
+gsk_vulkan_texture_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
+{
+ static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
+ {
+ .binding = 0,
+ .stride = sizeof (GskVulkanTextureInstance),
+ .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
+ {
+ .location = 0,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextureInstance, rect),
+ },
+ {
+ .location = 1,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32B32A32_SFLOAT,
+ .offset = G_STRUCT_OFFSET (GskVulkanTextureInstance, tex_rect),
+ }
+ };
+ static const VkPipelineVertexInputStateCreateInfo info = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
+ .pVertexBindingDescriptions = vertexBindingDescriptions,
+ .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
+ .pVertexAttributeDescriptions = vertexInputAttributeDescription
+ };
+
+ return &info;
+}
+
+static void
+gsk_vulkan_texture_pipeline_finalize (GObject *gobject)
+{
+ //GskVulkanTexturePipeline *self = GSK_VULKAN_TEXTURE_PIPELINE (gobject);
+
+ G_OBJECT_CLASS (gsk_vulkan_texture_pipeline_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_vulkan_texture_pipeline_class_init (GskVulkanTexturePipelineClass *klass)
+{
+ GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_texture_pipeline_finalize;
+
+ pipeline_class->get_input_state_create_info = gsk_vulkan_texture_pipeline_get_input_state_create_info;
+}
+
+static void
+gsk_vulkan_texture_pipeline_init (GskVulkanTexturePipeline *self)
+{
+}
+
+GskVulkanPipeline *
+gsk_vulkan_texture_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass)
+{
+ return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_TEXTURE_PIPELINE, context, layout, shader_name, render_pass);
+}
+
+gsize
+gsk_vulkan_texture_pipeline_count_vertex_data (GskVulkanTexturePipeline *pipeline)
+{
+ return sizeof (GskVulkanTextureInstance);
+}
+
+void
+gsk_vulkan_texture_pipeline_collect_vertex_data (GskVulkanTexturePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect)
+{
+ GskVulkanTextureInstance *instance = (GskVulkanTextureInstance *) data;
+
+ instance->rect[0] = rect->origin.x;
+ instance->rect[1] = rect->origin.y;
+ instance->rect[2] = rect->size.width;
+ instance->rect[3] = rect->size.height;
+ instance->tex_rect[0] = tex_rect->origin.x;
+ instance->tex_rect[1] = tex_rect->origin.y;
+ instance->tex_rect[2] = tex_rect->size.width;
+ instance->tex_rect[3] = tex_rect->size.height;
+}
+
+gsize
+gsk_vulkan_texture_pipeline_draw (GskVulkanTexturePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands)
+{
+ vkCmdDraw (command_buffer,
+ 6, n_commands,
+ 0, offset);
+
+ return n_commands;
+}
diff --git a/gsk/vulkan/gskvulkantexturepipelineprivate.h b/gsk/vulkan/gskvulkantexturepipelineprivate.h
new file mode 100644
index 0000000000..c6435c41ad
--- /dev/null
+++ b/gsk/vulkan/gskvulkantexturepipelineprivate.h
@@ -0,0 +1,33 @@
+#ifndef __GSK_VULKAN_TEXTURE_PIPELINE_PRIVATE_H__
+#define __GSK_VULKAN_TEXTURE_PIPELINE_PRIVATE_H__
+
+#include <graphene.h>
+
+#include "gskvulkanpipelineprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskVulkanTexturePipelineLayout GskVulkanTexturePipelineLayout;
+
+#define GSK_TYPE_VULKAN_TEXTURE_PIPELINE (gsk_vulkan_texture_pipeline_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskVulkanTexturePipeline, gsk_vulkan_texture_pipeline, GSK, VULKAN_TEXTURE_PIPELINE, GskVulkanPipeline)
+
+GskVulkanPipeline * gsk_vulkan_texture_pipeline_new (GdkVulkanContext *context,
+ VkPipelineLayout layout,
+ const char *shader_name,
+ VkRenderPass render_pass);
+
+gsize gsk_vulkan_texture_pipeline_count_vertex_data (GskVulkanTexturePipeline *pipeline);
+void gsk_vulkan_texture_pipeline_collect_vertex_data (GskVulkanTexturePipeline *pipeline,
+ guchar *data,
+ const graphene_rect_t *rect,
+ const graphene_rect_t *tex_rect);
+gsize gsk_vulkan_texture_pipeline_draw (GskVulkanTexturePipeline *pipeline,
+ VkCommandBuffer command_buffer,
+ gsize offset,
+ gsize n_commands);
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_TEXTURE_PIPELINE_PRIVATE_H__ */