diff --git a/gsk/Makefile.am b/gsk/Makefile.am index e176dd25c6..e74529c806 100644 --- a/gsk/Makefile.am +++ b/gsk/Makefile.am @@ -25,6 +25,7 @@ noinst_LTLIBRARIES = if HAVE_VULKAN gsk_private_vulkan_source_h = \ gskvulkanblendpipelineprivate.h \ + gskvulkanborderpipelineprivate.h \ gskvulkanbufferprivate.h \ gskvulkanclipprivate.h \ gskvulkancolorpipelineprivate.h \ @@ -41,6 +42,7 @@ gsk_private_vulkan_source_h = \ gskvulkanshaderprivate.h gsk_private_vulkan_source_c = \ gskvulkanblendpipeline.c \ + gskvulkanborderpipeline.c \ gskvulkanbuffer.c \ gskvulkanclip.c \ gskvulkancolorpipeline.c \ @@ -55,6 +57,8 @@ gsk_private_vulkan_source_c = \ gskvulkanrenderer.c \ gskvulkanrenderpass.c \ gskvulkanshader.c +gsk_private_vulkan_include_shaders = \ + resources/vulkan/rounded-rect.glsl gsk_private_vulkan_shaders = \ resources/vulkan/blend-clip.frag.glsl \ resources/vulkan/blend-clip-rounded.frag.glsl \ @@ -62,6 +66,12 @@ gsk_private_vulkan_shaders = \ resources/vulkan/blend-clip.vert.glsl \ resources/vulkan/blend.frag.glsl \ resources/vulkan/blend.vert.glsl \ + resources/vulkan/border-clip.frag.glsl \ + resources/vulkan/border-clip.vert.glsl \ + resources/vulkan/border-clip-rounded.frag.glsl \ + resources/vulkan/border-clip-rounded.vert.glsl \ + resources/vulkan/border.frag.glsl \ + resources/vulkan/border.vert.glsl \ resources/vulkan/color-clip.frag.glsl \ resources/vulkan/color-clip-rounded.frag.glsl \ resources/vulkan/color-clip-rounded.vert.glsl \ @@ -199,7 +209,7 @@ gskresources.c: gsk.resources.xml $(resource_files) $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< \ --target=$@ --sourcedir=$(srcdir) --c-name _gsk --generate-source --manual-register -EXTRA_DIST += $(resource_files) +EXTRA_DIST += $(resource_files) $(gsk_private_vulkan_include_shaders) CLEANFILES += gsk.resources.xml DISTCLEANFILES += gskresources.h gskresources.c diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index af356d5798..aaa6a922bd 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -593,22 +593,20 @@ gsk_border_node_peek_outline (GskRenderNode *node) return &self->outline; } -float -gsk_border_node_get_width (GskRenderNode *node, - guint i) +const float * +gsk_border_node_peek_widths (GskRenderNode *node) { GskBorderNode *self = (GskBorderNode *) node; - return self->border_width[i]; + return self->border_width; } const GdkRGBA * -gsk_border_node_peek_color (GskRenderNode *node, - guint i) +gsk_border_node_peek_colors (GskRenderNode *node) { GskBorderNode *self = (GskBorderNode *) node; - return &self->border_color[i]; + return self->border_color; } /** diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index 938c948002..52f7bc8b46 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -59,8 +59,8 @@ const gsize gsk_linear_gradient_node_get_n_color_stops (GskRenderNode *node); const GskColorStop * gsk_linear_gradient_node_peek_color_stops (GskRenderNode *node); const GskRoundedRect * gsk_border_node_peek_outline (GskRenderNode *node); -float gsk_border_node_get_width (GskRenderNode *node, guint i); -const GdkRGBA * gsk_border_node_peek_color (GskRenderNode *node, guint i); +const float * gsk_border_node_peek_widths (GskRenderNode *node); +const GdkRGBA * gsk_border_node_peek_colors (GskRenderNode *node); GskRenderNode *gsk_cairo_node_new_for_surface (const graphene_rect_t *bounds, cairo_surface_t *surface); cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node); diff --git a/gsk/gskvulkanborderpipeline.c b/gsk/gskvulkanborderpipeline.c new file mode 100644 index 0000000000..ab6509b785 --- /dev/null +++ b/gsk/gskvulkanborderpipeline.c @@ -0,0 +1,163 @@ +#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 (GskVulkanPipelineLayout *layout, + const char *shader_name, + VkRenderPass render_pass) +{ + return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BORDER_PIPELINE, 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/gskvulkanborderpipelineprivate.h b/gsk/gskvulkanborderpipelineprivate.h new file mode 100644 index 0000000000..de6d1768a7 --- /dev/null +++ b/gsk/gskvulkanborderpipelineprivate.h @@ -0,0 +1,34 @@ +#ifndef __GSK_VULKAN_BORDER_PIPELINE_PRIVATE_H__ +#define __GSK_VULKAN_BORDER_PIPELINE_PRIVATE_H__ + +#include + +#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 (GskVulkanPipelineLayout * 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/gskvulkanrender.c b/gsk/gskvulkanrender.c index 6c664ae367..cd362d00b1 100644 --- a/gsk/gskvulkanrender.c +++ b/gsk/gskvulkanrender.c @@ -9,6 +9,7 @@ #include "gskvulkanrenderpassprivate.h" #include "gskvulkanblendpipelineprivate.h" +#include "gskvulkanborderpipelineprivate.h" #include "gskvulkancolorpipelineprivate.h" #include "gskvulkaneffectpipelineprivate.h" #include "gskvulkanlineargradientpipelineprivate.h" @@ -326,7 +327,10 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self, { "linear-clip-rounded", gsk_vulkan_linear_gradient_pipeline_new }, { "color-matrix", gsk_vulkan_effect_pipeline_new }, { "color-matrix-clip", gsk_vulkan_effect_pipeline_new }, - { "color-matrix-clip-rounded", gsk_vulkan_effect_pipeline_new } + { "color-matrix-clip-rounded", gsk_vulkan_effect_pipeline_new }, + { "border", gsk_vulkan_border_pipeline_new }, + { "border-clip", gsk_vulkan_border_pipeline_new }, + { "border-clip-rounded", gsk_vulkan_border_pipeline_new } }; g_return_val_if_fail (type < GSK_VULKAN_N_PIPELINES, NULL); diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c index c500d908cc..c92375e30b 100644 --- a/gsk/gskvulkanrenderpass.c +++ b/gsk/gskvulkanrenderpass.c @@ -7,6 +7,7 @@ #include "gskrenderer.h" #include "gskroundedrectprivate.h" #include "gskvulkanblendpipelineprivate.h" +#include "gskvulkanborderpipelineprivate.h" #include "gskvulkanclipprivate.h" #include "gskvulkancolorpipelineprivate.h" #include "gskvulkaneffectpipelineprivate.h" @@ -30,6 +31,7 @@ typedef enum { GSK_VULKAN_OP_LINEAR_GRADIENT, GSK_VULKAN_OP_OPACITY, GSK_VULKAN_OP_COLOR_MATRIX, + GSK_VULKAN_OP_BORDER, /* GskVulkanOpPushConstants */ GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS } GskVulkanOpType; @@ -110,9 +112,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, case GSK_NOT_A_RENDER_NODE: g_assert_not_reached (); return; - case GSK_BORDER_NODE: case GSK_INSET_SHADOW_NODE: case GSK_OUTSET_SHADOW_NODE: + case GSK_REPEAT_NODE: case GSK_SHADOW_NODE: case GSK_BLEND_NODE: case GSK_CROSS_FADE_NODE: @@ -210,6 +212,20 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, 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; @@ -499,6 +515,7 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self, case GSK_VULKAN_OP_COLOR: case GSK_VULKAN_OP_LINEAR_GRADIENT: case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: + case GSK_VULKAN_OP_BORDER: break; } } @@ -543,6 +560,11 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self) 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; + default: g_assert_not_reached (); case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: @@ -644,6 +666,18 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self, } 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; + default: g_assert_not_reached (); case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: @@ -684,6 +718,7 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self, case GSK_VULKAN_OP_COLOR: case GSK_VULKAN_OP_LINEAR_GRADIENT: case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: + case GSK_VULKAN_OP_BORDER: break; } } @@ -829,6 +864,27 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self, 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_PUSH_VERTEX_CONSTANTS: gsk_vulkan_push_constants_push_vertex (&op->constants.constants, command_buffer, diff --git a/gsk/gskvulkanrenderprivate.h b/gsk/gskvulkanrenderprivate.h index 6bfb8b0573..ea9c751637 100644 --- a/gsk/gskvulkanrenderprivate.h +++ b/gsk/gskvulkanrenderprivate.h @@ -22,6 +22,9 @@ typedef enum { 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, /* add more */ GSK_VULKAN_N_PIPELINES } GskVulkanPipelineType; diff --git a/gsk/resources/vulkan/border-clip-rounded.frag.glsl b/gsk/resources/vulkan/border-clip-rounded.frag.glsl new file mode 100644 index 0000000000..bf4ad650b1 --- /dev/null +++ b/gsk/resources/vulkan/border-clip-rounded.frag.glsl @@ -0,0 +1,34 @@ +#version 420 core + +#include "rounded-rect.glsl" + +layout(location = 0) in vec2 inPos; +layout(location = 1) in vec4 inColor; +layout(location = 2) in vec4 inRect; +layout(location = 3) in vec4 inCornerWidths; +layout(location = 4) in vec4 inCornerHeights; +layout(location = 5) in vec4 inBorderWidths; +layout(location = 6) in flat vec4 inClipBounds; +layout(location = 7) in flat vec4 inClipWidths; +layout(location = 8) in flat vec4 inClipHeights; + +layout(location = 0) out vec4 color; + +vec4 +clip (vec4 color) +{ + RoundedRect r = RoundedRect (vec4(inClipBounds.xy, inClipBounds.xy + inClipBounds.zw), inClipWidths, inClipHeights); + + return color * rounded_rect_coverage (r, inPos); +} + +void main() +{ + RoundedRect routside = RoundedRect (vec4(inRect.xy, inRect.xy + inRect.zw), inCornerWidths, inCornerHeights); + RoundedRect rinside = rounded_rect_shrink (routside, inBorderWidths); + + float alpha = clamp (rounded_rect_coverage (routside, inPos) - + rounded_rect_coverage (rinside, inPos), + 0.0, 1.0); + color = clip (inColor); +} diff --git a/gsk/resources/vulkan/border-clip-rounded.frag.spv b/gsk/resources/vulkan/border-clip-rounded.frag.spv new file mode 100644 index 0000000000..401d7ae1c6 Binary files /dev/null and b/gsk/resources/vulkan/border-clip-rounded.frag.spv differ diff --git a/gsk/resources/vulkan/border-clip-rounded.vert.glsl b/gsk/resources/vulkan/border-clip-rounded.vert.glsl new file mode 100644 index 0000000000..8939a95d9b --- /dev/null +++ b/gsk/resources/vulkan/border-clip-rounded.vert.glsl @@ -0,0 +1,123 @@ +#version 420 core + +layout(location = 0) in vec4 inRect; +layout(location = 1) in vec4 inCornerWidths; +layout(location = 2) in vec4 inCornerHeights; +layout(location = 3) in vec4 inBorderWidths; +layout(location = 4) in mat4 inBorderColors; + +layout(push_constant) uniform PushConstants { + mat4 mvp; + vec4 clip_bounds; + vec4 clip_widths; + vec4 clip_heights; +} push; + +layout(location = 0) out vec2 outPos; +layout(location = 1) out flat vec4 outColor; +layout(location = 2) out flat vec4 outRect; +layout(location = 3) out flat vec4 outCornerWidths; +layout(location = 4) out flat vec4 outCornerHeights; +layout(location = 5) out flat vec4 outBorderWidths; +layout(location = 6) out flat vec4 outClipBounds; +layout(location = 7) out flat vec4 outClipWidths; +layout(location = 8) out flat vec4 outClipHeights; + +out gl_PerVertex { + vec4 gl_Position; +}; + +vec2 offsets[6] = { vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0) }; + +#define TOP 0 +#define RIGHT 1 +#define BOTTOM 2 +#define LEFT 3 + +#define TOP_LEFT 0 +#define TOP_RIGHT 1 +#define BOTTOM_RIGHT 2 +#define BOTTOM_LEFT 3 + +#define SLICE_TOP_LEFT 0 +#define SLICE_TOP 1 +#define SLICE_TOP_RIGHT 2 +#define SLICE_RIGHT 3 +#define SLICE_BOTTOM_RIGHT 4 +#define SLICE_BOTTOM 5 +#define SLICE_BOTTOM_LEFT 6 +#define SLICE_LEFT 7 + +vec4 intersect(vec4 a, vec4 b) +{ + a = vec4(a.xy, a.xy + a.zw); + b = vec4(b.xy, b.xy + b.zw); + vec4 result = vec4(max(a.xy, b.xy), min(a.zw, b.zw)); + if (any (greaterThanEqual (result.xy, result.zw))) + return vec4(0.0,0.0,0.0,0.0); + return vec4(result.xy, result.zw - result.xy); +} + +void main() { + int slice_index = gl_VertexIndex / 6; + int vert_index = gl_VertexIndex % 6; + + vec4 corner_widths = max (inCornerWidths, inBorderWidths.wyyw); + vec4 corner_heights = max (inCornerHeights, inBorderWidths.xxzz); + + vec4 rect; + + switch (slice_index) + { + case SLICE_TOP_LEFT: + rect = vec4(inRect.xy, corner_widths[TOP_LEFT], corner_heights[TOP_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_TOP: + rect = vec4(inRect.x + corner_widths[TOP_LEFT], inRect.y, inRect.z - corner_widths[TOP_LEFT] - corner_widths[TOP_RIGHT], inBorderWidths[TOP]); + outColor = inBorderColors[TOP]; + break; + case SLICE_TOP_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[TOP_RIGHT], inRect.y, corner_widths[TOP_RIGHT], corner_heights[TOP_RIGHT]); + break; + case SLICE_RIGHT: + rect = vec4(inRect.x + inRect.z - inBorderWidths[RIGHT], inRect.y + corner_heights[TOP_RIGHT], inBorderWidths[RIGHT], inRect.w - corner_heights[TOP_RIGHT] - corner_heights[BOTTOM_RIGHT]); + outColor = inBorderColors[RIGHT]; + break; + case SLICE_BOTTOM_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[BOTTOM_RIGHT], inRect.y + inRect.w - corner_heights[BOTTOM_RIGHT], corner_widths[BOTTOM_RIGHT], corner_heights[BOTTOM_RIGHT]); + break; + case SLICE_BOTTOM: + rect = vec4(inRect.x + corner_widths[BOTTOM_LEFT], inRect.y + inRect.w - inBorderWidths[BOTTOM], inRect.z - corner_widths[BOTTOM_LEFT] - corner_widths[BOTTOM_RIGHT], inBorderWidths[BOTTOM]); + break; + case SLICE_BOTTOM_LEFT: + rect = vec4(inRect.x, inRect.y + inRect.w - corner_heights[BOTTOM_LEFT], corner_widths[BOTTOM_LEFT], corner_heights[BOTTOM_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_LEFT: + rect = vec4(inRect.x, inRect.y + corner_heights[TOP_LEFT], inBorderWidths[LEFT], inRect.w - corner_heights[TOP_LEFT] - corner_heights[BOTTOM_LEFT]); + break; + } + + rect = intersect (rect, push.clip_bounds); + vec2 pos; + if ((slice_index % 4) != 0 || (vert_index % 3) != 2) + pos = rect.xy + rect.zw * offsets[vert_index]; + else + pos = rect.xy + rect.zw * vec2(1.0 - offsets[vert_index].x, offsets[vert_index].y); + gl_Position = push.mvp * vec4 (pos, 0.0, 1.0); + outColor = inBorderColors[((gl_VertexIndex / 3 + 15) / 4) % 4]; + outPos = pos; + outRect = inRect; + outCornerWidths = inCornerWidths; + outCornerHeights = inCornerHeights; + outBorderWidths = inBorderWidths; + outClipBounds = push.clip_bounds; + outClipWidths = push.clip_widths; + outClipHeights = push.clip_heights; +} diff --git a/gsk/resources/vulkan/border-clip-rounded.vert.spv b/gsk/resources/vulkan/border-clip-rounded.vert.spv new file mode 100644 index 0000000000..5aae91ae71 Binary files /dev/null and b/gsk/resources/vulkan/border-clip-rounded.vert.spv differ diff --git a/gsk/resources/vulkan/border-clip.frag.glsl b/gsk/resources/vulkan/border-clip.frag.glsl new file mode 100644 index 0000000000..5f9d7090a1 --- /dev/null +++ b/gsk/resources/vulkan/border-clip.frag.glsl @@ -0,0 +1,23 @@ +#version 420 core + +#include "rounded-rect.glsl" + +layout(location = 0) in vec2 inPos; +layout(location = 1) in flat vec4 inColor; +layout(location = 2) in flat vec4 inRect; +layout(location = 3) in flat vec4 inCornerWidths; +layout(location = 4) in flat vec4 inCornerHeights; +layout(location = 5) in flat vec4 inBorderWidths; + +layout(location = 0) out vec4 color; + +void main() +{ + RoundedRect routside = RoundedRect (vec4(inRect.xy, inRect.xy + inRect.zw), inCornerWidths, inCornerHeights); + RoundedRect rinside = rounded_rect_shrink (routside, inBorderWidths); + + float alpha = clamp (rounded_rect_coverage (routside, inPos) - + rounded_rect_coverage (rinside, inPos), + 0.0, 1.0); + color = inColor * alpha; +} diff --git a/gsk/resources/vulkan/border-clip.frag.spv b/gsk/resources/vulkan/border-clip.frag.spv new file mode 100644 index 0000000000..ab43d3dec8 Binary files /dev/null and b/gsk/resources/vulkan/border-clip.frag.spv differ diff --git a/gsk/resources/vulkan/border-clip.vert.glsl b/gsk/resources/vulkan/border-clip.vert.glsl new file mode 100644 index 0000000000..5de7a12f70 --- /dev/null +++ b/gsk/resources/vulkan/border-clip.vert.glsl @@ -0,0 +1,117 @@ +#version 420 core + +layout(location = 0) in vec4 inRect; +layout(location = 1) in vec4 inCornerWidths; +layout(location = 2) in vec4 inCornerHeights; +layout(location = 3) in vec4 inBorderWidths; +layout(location = 4) in mat4 inBorderColors; + +layout(push_constant) uniform PushConstants { + mat4 mvp; + vec4 clip_bounds; + vec4 clip_widths; + vec4 clip_heights; +} push; + +layout(location = 0) out vec2 outPos; +layout(location = 1) out flat vec4 outColor; +layout(location = 2) out flat vec4 outRect; +layout(location = 3) out flat vec4 outCornerWidths; +layout(location = 4) out flat vec4 outCornerHeights; +layout(location = 5) out flat vec4 outBorderWidths; + +out gl_PerVertex { + vec4 gl_Position; +}; + +vec2 offsets[6] = { vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0) }; + +#define TOP 0 +#define RIGHT 1 +#define BOTTOM 2 +#define LEFT 3 + +#define TOP_LEFT 0 +#define TOP_RIGHT 1 +#define BOTTOM_RIGHT 2 +#define BOTTOM_LEFT 3 + +#define SLICE_TOP_LEFT 0 +#define SLICE_TOP 1 +#define SLICE_TOP_RIGHT 2 +#define SLICE_RIGHT 3 +#define SLICE_BOTTOM_RIGHT 4 +#define SLICE_BOTTOM 5 +#define SLICE_BOTTOM_LEFT 6 +#define SLICE_LEFT 7 + +vec4 intersect(vec4 a, vec4 b) +{ + a = vec4(a.xy, a.xy + a.zw); + b = vec4(b.xy, b.xy + b.zw); + vec4 result = vec4(max(a.xy, b.xy), min(a.zw, b.zw)); + if (any (greaterThanEqual (result.xy, result.zw))) + return vec4(0.0,0.0,0.0,0.0); + return vec4(result.xy, result.zw - result.xy); +} + +void main() { + int slice_index = gl_VertexIndex / 6; + int vert_index = gl_VertexIndex % 6; + + vec4 corner_widths = max (inCornerWidths, inBorderWidths.wyyw); + vec4 corner_heights = max (inCornerHeights, inBorderWidths.xxzz); + + vec4 rect; + + switch (slice_index) + { + case SLICE_TOP_LEFT: + rect = vec4(inRect.xy, corner_widths[TOP_LEFT], corner_heights[TOP_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_TOP: + rect = vec4(inRect.x + corner_widths[TOP_LEFT], inRect.y, inRect.z - corner_widths[TOP_LEFT] - corner_widths[TOP_RIGHT], inBorderWidths[TOP]); + outColor = inBorderColors[TOP]; + break; + case SLICE_TOP_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[TOP_RIGHT], inRect.y, corner_widths[TOP_RIGHT], corner_heights[TOP_RIGHT]); + break; + case SLICE_RIGHT: + rect = vec4(inRect.x + inRect.z - inBorderWidths[RIGHT], inRect.y + corner_heights[TOP_RIGHT], inBorderWidths[RIGHT], inRect.w - corner_heights[TOP_RIGHT] - corner_heights[BOTTOM_RIGHT]); + outColor = inBorderColors[RIGHT]; + break; + case SLICE_BOTTOM_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[BOTTOM_RIGHT], inRect.y + inRect.w - corner_heights[BOTTOM_RIGHT], corner_widths[BOTTOM_RIGHT], corner_heights[BOTTOM_RIGHT]); + break; + case SLICE_BOTTOM: + rect = vec4(inRect.x + corner_widths[BOTTOM_LEFT], inRect.y + inRect.w - inBorderWidths[BOTTOM], inRect.z - corner_widths[BOTTOM_LEFT] - corner_widths[BOTTOM_RIGHT], inBorderWidths[BOTTOM]); + break; + case SLICE_BOTTOM_LEFT: + rect = vec4(inRect.x, inRect.y + inRect.w - corner_heights[BOTTOM_LEFT], corner_widths[BOTTOM_LEFT], corner_heights[BOTTOM_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_LEFT: + rect = vec4(inRect.x, inRect.y + corner_heights[TOP_LEFT], inBorderWidths[LEFT], inRect.w - corner_heights[TOP_LEFT] - corner_heights[BOTTOM_LEFT]); + break; + } + + rect = intersect (rect, push.clip_bounds); + vec2 pos; + if ((slice_index % 4) != 0 || (vert_index % 3) != 2) + pos = rect.xy + rect.zw * offsets[vert_index]; + else + pos = rect.xy + rect.zw * vec2(1.0 - offsets[vert_index].x, offsets[vert_index].y); + gl_Position = push.mvp * vec4 (pos, 0.0, 1.0); + outColor = inBorderColors[((gl_VertexIndex / 3 + 15) / 4) % 4]; + outPos = pos; + outRect = inRect; + outCornerWidths = inCornerWidths; + outCornerHeights = inCornerHeights; + outBorderWidths = inBorderWidths; +} diff --git a/gsk/resources/vulkan/border-clip.vert.spv b/gsk/resources/vulkan/border-clip.vert.spv new file mode 100644 index 0000000000..5a505d8ebc Binary files /dev/null and b/gsk/resources/vulkan/border-clip.vert.spv differ diff --git a/gsk/resources/vulkan/border.frag.glsl b/gsk/resources/vulkan/border.frag.glsl new file mode 100644 index 0000000000..5f9d7090a1 --- /dev/null +++ b/gsk/resources/vulkan/border.frag.glsl @@ -0,0 +1,23 @@ +#version 420 core + +#include "rounded-rect.glsl" + +layout(location = 0) in vec2 inPos; +layout(location = 1) in flat vec4 inColor; +layout(location = 2) in flat vec4 inRect; +layout(location = 3) in flat vec4 inCornerWidths; +layout(location = 4) in flat vec4 inCornerHeights; +layout(location = 5) in flat vec4 inBorderWidths; + +layout(location = 0) out vec4 color; + +void main() +{ + RoundedRect routside = RoundedRect (vec4(inRect.xy, inRect.xy + inRect.zw), inCornerWidths, inCornerHeights); + RoundedRect rinside = rounded_rect_shrink (routside, inBorderWidths); + + float alpha = clamp (rounded_rect_coverage (routside, inPos) - + rounded_rect_coverage (rinside, inPos), + 0.0, 1.0); + color = inColor * alpha; +} diff --git a/gsk/resources/vulkan/border.frag.spv b/gsk/resources/vulkan/border.frag.spv new file mode 100644 index 0000000000..ab43d3dec8 Binary files /dev/null and b/gsk/resources/vulkan/border.frag.spv differ diff --git a/gsk/resources/vulkan/border.vert.glsl b/gsk/resources/vulkan/border.vert.glsl new file mode 100644 index 0000000000..48efbc7e40 --- /dev/null +++ b/gsk/resources/vulkan/border.vert.glsl @@ -0,0 +1,106 @@ +#version 420 core + +layout(location = 0) in vec4 inRect; +layout(location = 1) in vec4 inCornerWidths; +layout(location = 2) in vec4 inCornerHeights; +layout(location = 3) in vec4 inBorderWidths; +layout(location = 4) in mat4 inBorderColors; + +layout(push_constant) uniform PushConstants { + mat4 mvp; + vec4 clip_bounds; + vec4 clip_widths; + vec4 clip_heights; +} push; + +layout(location = 0) out vec2 outPos; +layout(location = 1) out flat vec4 outColor; +layout(location = 2) out flat vec4 outRect; +layout(location = 3) out flat vec4 outCornerWidths; +layout(location = 4) out flat vec4 outCornerHeights; +layout(location = 5) out flat vec4 outBorderWidths; + +out gl_PerVertex { + vec4 gl_Position; +}; + +vec2 offsets[6] = { vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0) }; + +#define TOP 0 +#define RIGHT 1 +#define BOTTOM 2 +#define LEFT 3 + +#define TOP_LEFT 0 +#define TOP_RIGHT 1 +#define BOTTOM_RIGHT 2 +#define BOTTOM_LEFT 3 + +#define SLICE_TOP_LEFT 0 +#define SLICE_TOP 1 +#define SLICE_TOP_RIGHT 2 +#define SLICE_RIGHT 3 +#define SLICE_BOTTOM_RIGHT 4 +#define SLICE_BOTTOM 5 +#define SLICE_BOTTOM_LEFT 6 +#define SLICE_LEFT 7 + +void main() { + int slice_index = gl_VertexIndex / 6; + int vert_index = gl_VertexIndex % 6; + + vec4 corner_widths = max (inCornerWidths, inBorderWidths.wyyw); + vec4 corner_heights = max (inCornerHeights, inBorderWidths.xxzz); + + vec4 rect; + + switch (slice_index) + { + case SLICE_TOP_LEFT: + rect = vec4(inRect.xy, corner_widths[TOP_LEFT], corner_heights[TOP_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_TOP: + rect = vec4(inRect.x + corner_widths[TOP_LEFT], inRect.y, inRect.z - corner_widths[TOP_LEFT] - corner_widths[TOP_RIGHT], inBorderWidths[TOP]); + outColor = inBorderColors[TOP]; + break; + case SLICE_TOP_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[TOP_RIGHT], inRect.y, corner_widths[TOP_RIGHT], corner_heights[TOP_RIGHT]); + break; + case SLICE_RIGHT: + rect = vec4(inRect.x + inRect.z - inBorderWidths[RIGHT], inRect.y + corner_heights[TOP_RIGHT], inBorderWidths[RIGHT], inRect.w - corner_heights[TOP_RIGHT] - corner_heights[BOTTOM_RIGHT]); + outColor = inBorderColors[RIGHT]; + break; + case SLICE_BOTTOM_RIGHT: + rect = vec4(inRect.x + inRect.z - corner_widths[BOTTOM_RIGHT], inRect.y + inRect.w - corner_heights[BOTTOM_RIGHT], corner_widths[BOTTOM_RIGHT], corner_heights[BOTTOM_RIGHT]); + break; + case SLICE_BOTTOM: + rect = vec4(inRect.x + corner_widths[BOTTOM_LEFT], inRect.y + inRect.w - inBorderWidths[BOTTOM], inRect.z - corner_widths[BOTTOM_LEFT] - corner_widths[BOTTOM_RIGHT], inBorderWidths[BOTTOM]); + break; + case SLICE_BOTTOM_LEFT: + rect = vec4(inRect.x, inRect.y + inRect.w - corner_heights[BOTTOM_LEFT], corner_widths[BOTTOM_LEFT], corner_heights[BOTTOM_LEFT]); + vert_index = (vert_index + 3) % 6; + break; + case SLICE_LEFT: + rect = vec4(inRect.x, inRect.y + corner_heights[TOP_LEFT], inBorderWidths[LEFT], inRect.w - corner_heights[TOP_LEFT] - corner_heights[BOTTOM_LEFT]); + break; + } + + vec2 pos; + if ((slice_index % 4) != 0 || (vert_index % 3) != 2) + pos = rect.xy + rect.zw * offsets[vert_index]; + else + pos = rect.xy + rect.zw * vec2(1.0 - offsets[vert_index].x, offsets[vert_index].y); + gl_Position = push.mvp * vec4 (pos, 0.0, 1.0); + outColor = inBorderColors[((gl_VertexIndex / 3 + 15) / 4) % 4]; + outPos = pos; + outRect = inRect; + outCornerWidths = inCornerWidths; + outCornerHeights = inCornerHeights; + outBorderWidths = inBorderWidths; +} diff --git a/gsk/resources/vulkan/border.vert.spv b/gsk/resources/vulkan/border.vert.spv new file mode 100644 index 0000000000..384eebeae8 Binary files /dev/null and b/gsk/resources/vulkan/border.vert.spv differ diff --git a/gsk/resources/vulkan/rounded-rect.glsl b/gsk/resources/vulkan/rounded-rect.glsl new file mode 100644 index 0000000000..e0c2ffc6f9 --- /dev/null +++ b/gsk/resources/vulkan/rounded-rect.glsl @@ -0,0 +1,69 @@ +#ifndef _ROUNDED_RECT_ +#define _ROUNDED_RECT_ + +struct RoundedRect +{ + vec4 bounds; + vec4 corner_widths; + vec4 corner_heights; +}; + +float +ellipsis_dist (vec2 p, vec2 radius) +{ + vec2 p0 = p / radius; + vec2 p1 = 2.0 * p0 / radius; + + return (dot(p0, p0) - 1.0) / length (p1); +} + +float +ellipsis_coverage (vec2 point, vec2 center, vec2 radius) +{ + float d = ellipsis_dist (point - center, radius); + return clamp (0.5 - d, 0.0, 1.0); +} + +float +rounded_rect_coverage (RoundedRect r, vec2 p) +{ + if (p.x < r.bounds.x || p.y < r.bounds.y || + p.x >= r.bounds.z || p.y >= r.bounds.w) + return 0.0; + + vec2 rad_tl = vec2(r.corner_widths.x, r.corner_heights.x); + vec2 rad_tr = vec2(r.corner_widths.y, r.corner_heights.y); + vec2 rad_br = vec2(r.corner_widths.z, r.corner_heights.z); + vec2 rad_bl = vec2(r.corner_widths.w, r.corner_heights.w); + + vec2 ref_tl = r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x); + vec2 ref_tr = r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y); + vec2 ref_br = r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z); + vec2 ref_bl = r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w); + + float d_tl = ellipsis_coverage(p, ref_tl, rad_tl); + float d_tr = ellipsis_coverage(p, ref_tr, rad_tr); + float d_br = ellipsis_coverage(p, ref_br, rad_br); + float d_bl = ellipsis_coverage(p, ref_bl, rad_bl); + + vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); + + bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, + p.x > ref_tr.x && p.y < ref_tr.y, + p.x > ref_br.x && p.y > ref_br.y, + p.x < ref_bl.x && p.y > ref_bl.y); + + return 1.0 - dot(vec4(is_out), corner_coverages); +} + +RoundedRect +rounded_rect_shrink (RoundedRect r, vec4 amount) +{ + vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; + vec4 new_widths = max (r.corner_widths - amount.wyyw, 0.0); + vec4 new_heights = max (r.corner_heights - amount.xxzz, 0.0); + + return RoundedRect (new_bounds, new_widths, new_heights); +} + +#endif