[gtk+] vulkan: Add shader for border rendering



commit b186bce7ff4febcb3fd7cd77a0c40ab7dcb4886b
Author: Benjamin Otte <otte redhat com>
Date:   Tue Jan 10 14:59:20 2017 +0100

    vulkan: Add shader for border rendering

 gsk/Makefile.am                                    |   12 ++-
 gsk/gskrendernodeimpl.c                            |   12 +-
 gsk/gskrendernodeprivate.h                         |    4 +-
 gsk/gskvulkanborderpipeline.c                      |  163 ++++++++++++++++++++
 gsk/gskvulkanborderpipelineprivate.h               |   34 ++++
 gsk/gskvulkanrender.c                              |    6 +-
 gsk/gskvulkanrenderpass.c                          |   58 +++++++-
 gsk/gskvulkanrenderprivate.h                       |    3 +
 gsk/resources/vulkan/border-clip-rounded.frag.glsl |   34 ++++
 gsk/resources/vulkan/border-clip-rounded.frag.spv  |  Bin 0 -> 9832 bytes
 gsk/resources/vulkan/border-clip-rounded.vert.glsl |  123 +++++++++++++++
 gsk/resources/vulkan/border-clip-rounded.vert.spv  |  Bin 0 -> 9312 bytes
 gsk/resources/vulkan/border-clip.frag.glsl         |   23 +++
 gsk/resources/vulkan/border-clip.frag.spv          |  Bin 0 -> 9008 bytes
 gsk/resources/vulkan/border-clip.vert.glsl         |  117 ++++++++++++++
 gsk/resources/vulkan/border-clip.vert.spv          |  Bin 0 -> 8952 bytes
 gsk/resources/vulkan/border.frag.glsl              |   23 +++
 gsk/resources/vulkan/border.frag.spv               |  Bin 0 -> 9008 bytes
 gsk/resources/vulkan/border.vert.glsl              |  106 +++++++++++++
 gsk/resources/vulkan/border.vert.spv               |  Bin 0 -> 7216 bytes
 gsk/resources/vulkan/rounded-rect.glsl             |   69 ++++++++
 21 files changed, 775 insertions(+), 12 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index e176dd2..e74529c 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 af356d5..aaa6a92 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 938c948..52f7bc8 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 0000000..ab6509b
--- /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 0000000..de6d176
--- /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 <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                  (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 6c664ae..cd362d0 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 c500d90..c92375e 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 6bfb8b0..ea9c751 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 0000000..bf4ad65
--- /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 0000000..401d7ae
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 0000000..8939a95
--- /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 0000000..5aae91a
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 0000000..5f9d709
--- /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 0000000..ab43d3d
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 0000000..5de7a12
--- /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 0000000..5a505d8
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 0000000..5f9d709
--- /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 0000000..ab43d3d
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 0000000..48efbc7
--- /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 0000000..384eebe
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 0000000..e0c2ffc
--- /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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]