diff options
author | Matthias Clasen <mclasen@redhat.com> | 2018-01-06 09:36:55 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2018-01-06 09:36:55 -0500 |
commit | 3771c95c72d591d3ed6ecf143fa85e1fea0a3d11 (patch) | |
tree | fb66e92aee87e28bee3c363a84f7874f763b7bda /gsk/vulkan | |
parent | 3c38ebb906319cb75688f9929b65ecf2a4fd3440 (diff) | |
download | gtk+-3771c95c72d591d3ed6ecf143fa85e1fea0a3d11.tar.gz |
gsk: Move Vulkan sources to a subdirectory
Following what was already done for GL.
Diffstat (limited to 'gsk/vulkan')
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, ®ions[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 (¤t_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__ */ |