vulkan: Make gradient shader use buffers
authorBenjamin Otte <otte@redhat.com>
Thu, 8 Jun 2023 10:03:48 +0000 (12:03 +0200)
committerBenjamin Otte <otte@redhat.com>
Thu, 8 Jun 2023 19:53:06 +0000 (21:53 +0200)
This allows putting any number of color stops into the buffer, so
fallbacks with too many stops are no longer necessary.

gsk/vulkan/gskvulkanlineargradientpipeline.c
gsk/vulkan/gskvulkanlineargradientpipelineprivate.h
gsk/vulkan/gskvulkanrenderpass.c
gsk/vulkan/resources/linear.frag
gsk/vulkan/resources/linear.vert

index 79de43720c5d7802c54c78b1e850fcbf5dccb0f2..1a790de475ca47df7d247243d7e6249151c4cba9 100644 (file)
@@ -15,9 +15,8 @@ struct _GskVulkanLinearGradientInstance
   float start[2];
   float end[2];
   gint32 repeating;
+  gint32 offset;
   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)
@@ -61,67 +60,13 @@ gsk_vulkan_linear_gradient_pipeline_get_input_state_create_info (GskVulkanPipeli
           .location = 4,
           .binding = 0,
           .format = VK_FORMAT_R32_SINT,
-          .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count),
+          .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offset),
       },
       {
           .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]),
+          .format = VK_FORMAT_R32_SINT,
+          .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count),
       }
   };
   static const VkPipelineVertexInputStateCreateInfo info = {
@@ -169,23 +114,17 @@ gsk_vulkan_linear_gradient_pipeline_new (GdkVulkanContext        *context,
 
 void
 gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradientPipeline *pipeline,
-                                                         guchar                    *data,
-                                                         const graphene_point_t    *offset,
-                                                         const graphene_rect_t     *rect,
-                                                         const graphene_point_t    *start,
-                                                         const graphene_point_t    *end,
-                                                         gboolean                   repeating,
-                                                         gsize                      n_stops,
-                                                         const GskColorStop        *stops)
+                                                         guchar                          *data,
+                                                         const graphene_point_t          *offset,
+                                                         const graphene_rect_t           *rect,
+                                                         const graphene_point_t          *start,
+                                                         const graphene_point_t          *end,
+                                                         gboolean                         repeating,
+                                                         gsize                            gradient_offset,
+                                                         gsize                            n_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 + offset->x;
   instance->rect[1] = rect->origin.y + offset->y;
   instance->rect[2] = rect->size.width;
@@ -195,15 +134,8 @@ gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradient
   instance->end[0] = end->x + offset->x;
   instance->end[1] = end->y + offset->y;
   instance->repeating = repeating;
+  instance->offset = gradient_offset;
   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
index f33448a91faed5eb4e509547d7b13c3c2e1eab5c..21bf10066fea3de27443e4984e64f7a97a1fcadb 100644 (file)
@@ -28,8 +28,8 @@ void                    gsk_vulkan_linear_gradient_pipeline_collect_vertex_data
                                                                          const graphene_point_t         *start,
                                                                          const graphene_point_t         *end,
                                                                          gboolean                        repeating,
-                                                                         gsize                           n_stops,
-                                                                         const GskColorStop             *stops);
+                                                                         gsize                           gradient_offset,
+                                                                         gsize                           n_stops);
 gsize                   gsk_vulkan_linear_gradient_pipeline_draw        (GskVulkanLinearGradientPipeline*pipeline,
                                                                          VkCommandBuffer                 command_buffer,
                                                                          gsize                           offset,
index cacbfd3e4cd5072d6523c2c242cd0cd6504644c2..67338b74cdac2ba3aa7a1e94c9f18d81795ff1ca 100644 (file)
@@ -77,6 +77,7 @@ struct _GskVulkanOpRender
   gsize                vertex_offset; /* offset into vertex buffer */
   guint32              image_descriptor[2]; /* index into descriptor for the (image, sampler) */
   guint32              image_descriptor2[2]; /* index into descriptor for the 2nd image (if relevant) */
+  gsize                buffer_offset; /* offset into buffer */
   graphene_rect_t      source_rect; /* area that source maps to */
   graphene_rect_t      source2_rect; /* area that source2 maps to */
 };
@@ -406,10 +407,10 @@ gsk_vulkan_render_pass_add_color_node (GskVulkanRenderPass       *self,
 }
 
 static inline gboolean
-gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass       *self,
-                                                           GskVulkanRender           *render,
-                                                           const GskVulkanParseState *state,
-                                                           GskRenderNode             *node)
+gsk_vulkan_render_pass_add_linear_gradient_node (GskVulkanRenderPass       *self,
+                                                 GskVulkanRender           *render,
+                                                 const GskVulkanParseState *state,
+                                                 GskRenderNode             *node)
 {
   GskVulkanPipelineType pipeline_type;
   GskVulkanOp op = {
@@ -418,11 +419,6 @@ gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass
     .render.offset = state->offset,
   };
 
-  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",
-              gsk_linear_gradient_node_get_n_color_stops (node),
-              GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS);
-
   if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
     pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT;
   else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
@@ -1138,8 +1134,8 @@ static const GskVulkanRenderPassNodeFunc nodes_vtable[N_RENDER_NODES] = {
   [GSK_CONTAINER_NODE] = gsk_vulkan_render_pass_add_container_node,
   [GSK_CAIRO_NODE] = gsk_vulkan_render_pass_add_cairo_node,
   [GSK_COLOR_NODE] = gsk_vulkan_render_pass_add_color_node,
-  [GSK_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_linear_gradient_node,
-  [GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_linear_gradient_node,
+  [GSK_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
+  [GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
   [GSK_RADIAL_GRADIENT_NODE] = NULL,
   [GSK_REPEATING_RADIAL_GRADIENT_NODE] = NULL,
   [GSK_CONIC_GRADIENT_NODE] = NULL,
@@ -1896,8 +1892,8 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
                                                                    gsk_linear_gradient_node_get_start (op->render.node),
                                                                    gsk_linear_gradient_node_get_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_get_color_stops (op->render.node, NULL));
+                                                                   op->render.buffer_offset,
+                                                                   gsk_linear_gradient_node_get_n_color_stops (op->render.node));
           break;
 
         case GSK_VULKAN_OP_OPACITY:
@@ -2125,11 +2121,25 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
             }
           break;
 
+        case GSK_VULKAN_OP_LINEAR_GRADIENT:
+          {
+            gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (op->render.node);
+            guchar *mem;
+
+            mem = gsk_vulkan_render_get_buffer_memory (render,
+                                                       n_stops * sizeof (GskColorStop),
+                                                       G_ALIGNOF (GskColorStop),
+                                                       &op->render.buffer_offset);
+            memcpy (mem,
+                    gsk_linear_gradient_node_get_color_stops (op->render.node, NULL),
+                    n_stops * sizeof (GskColorStop));
+          }
+          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:
index cc62e58aac6e2d1c1e9aaf943ea4d78c136e8f94..d23543085f380c2f6dbeae9ab493b55d55474fe4 100644 (file)
@@ -1,6 +1,8 @@
 #version 450
 
+#include "common.frag.glsl"
 #include "clip.frag.glsl"
+#include "rect.frag.glsl"
 
 struct ColorStop {
   float offset;
@@ -8,29 +10,55 @@ struct ColorStop {
 };
 
 layout(location = 0) in vec2 inPos;
-layout(location = 1) in float inGradientPos;
-layout(location = 2) in flat int inRepeating;
-layout(location = 3) in flat int inStopCount;
-layout(location = 4) in flat ColorStop inStops[8];
+layout(location = 1) in flat Rect inRect;
+layout(location = 2) in float inGradientPos;
+layout(location = 3) in flat int inRepeating;
+layout(location = 4) in flat int inStopOffset;
+layout(location = 5) in flat int inStopCount;
 
-layout(location = 0) out vec4 outColor;
+layout(location = 0) out vec4 color;
+
+ColorStop
+get_stop(int i)
+{
+  ColorStop result;
+
+  result.offset = get_float(inStopOffset + i * 5);
+  result.color = vec4(get_float(inStopOffset + i * 5 + 1),
+                      get_float(inStopOffset + i * 5 + 2),
+                      get_float(inStopOffset + i * 5 + 3),
+                      get_float(inStopOffset + i * 5 + 4));
+
+  return result;
+}
 
 void main()
 {
   float pos;
+
   if (inRepeating != 0)
     pos = fract (inGradientPos);
   else
     pos = clamp (inGradientPos, 0, 1);
 
-  vec4 color = inStops[0].color;
-  int n = clamp (inStopCount, 2, 8);
-  for (int i = 1; i < n; i++)
+  ColorStop stop = get_stop (0);
+  float last_offset = stop.offset;
+  color = stop.color;
+  for (int i = 1; i < inStopCount; i++)
     {
-      if (inStops[i].offset > inStops[i-1].offset)
-        color = mix (color, inStops[i].color, clamp((pos - inStops[i-1].offset) / (inStops[i].offset - inStops[i-1].offset), 0, 1));
+      stop = get_stop(i);
+      if (stop.offset < pos)
+        color = stop.color;
+      else
+        color = mix (color, stop.color, clamp((pos - last_offset) / (stop.offset - last_offset), 0, 1));
+      last_offset = stop.offset;
+      if (last_offset >= pos)
+        break;
     }
   
-  //outColor = vec4(pos, pos, pos, 1.0);
-  outColor = clip (inPos, color);
+  if (last_offset < pos)
+    color = mix (color, stop.color, clamp((pos - last_offset) / (1 - last_offset), 0, 1));
+
+  float alpha = color.a * rect_coverage (inRect, inPos);
+  color = clip_scaled (inPos, vec4(color.rgb, 1) * alpha);
 }
index c0271f2ebc925bf28d068872f96412ae221d54d5..d2cb76fed9b23bc0cf347dbdba0db6d4f8a57e14 100644 (file)
@@ -1,6 +1,7 @@
 #version 450
 
-#include "clip.vert.glsl"
+#include "common.vert.glsl"
+#include "rect.vert.glsl"
 
 struct ColorStop {
   float offset;
@@ -11,62 +12,32 @@ layout(location = 0) in vec4 inRect;
 layout(location = 1) in vec2 inStart;
 layout(location = 2) in vec2 inEnd;
 layout(location = 3) in int inRepeating;
-layout(location = 4) in int inStopCount;
-layout(location = 5) in vec4 inOffsets0;
-layout(location = 6) in vec4 inOffsets1;
-layout(location = 7) in vec4 inColors0;
-layout(location = 8) in vec4 inColors1;
-layout(location = 9) in vec4 inColors2;
-layout(location = 10) in vec4 inColors3;
-layout(location = 11) in vec4 inColors4;
-layout(location = 12) in vec4 inColors5;
-layout(location = 13) in vec4 inColors6;
-layout(location = 14) in vec4 inColors7;
+layout(location = 4) in int inStopOffset;
+layout(location = 5) in int inStopCount;
 
 layout(location = 0) out vec2 outPos;
-layout(location = 1) out float outGradientPos;
-layout(location = 2) out flat int outRepeating;
-layout(location = 3) out flat int outStopCount;
-layout(location = 4) out flat ColorStop outStops[8];
-
-vec2 offsets[6] = { vec2(0.0, 0.0),
-                    vec2(1.0, 0.0),
-                    vec2(0.0, 1.0),
-                    vec2(0.0, 1.0),
-                    vec2(1.0, 0.0),
-                    vec2(1.0, 1.0) };
+layout(location = 1) out flat Rect outRect;
+layout(location = 2) out float outGradientPos;
+layout(location = 3) out flat int outRepeating;
+layout(location = 4) out flat int outStopOffset;
+layout(location = 5) out flat int outStopCount;
 
 float
 get_gradient_pos (vec2 pos)
 {
-  pos = pos - inStart;
-  vec2 grad = inEnd - inStart;
+  pos = pos - inStart * push.scale;
+  vec2 grad = (inEnd - inStart) * push.scale;
 
   return dot (pos, grad) / dot (grad, grad);
 }
 
 void main() {
-  vec4 rect = clip (inRect);
-  vec2 pos = rect.xy + rect.zw * offsets[gl_VertexIndex];
-  gl_Position = push.mvp * vec4 (push.scale * pos, 0.0, 1.0);
+  Rect r = rect_from_gsk (inRect);
+  vec2 pos = set_position_from_rect (r);
   outPos = pos;
+  outRect = r;
   outGradientPos = get_gradient_pos (pos);
   outRepeating = inRepeating;
+  outStopOffset = inStopOffset;
   outStopCount = inStopCount;
-  outStops[0].offset = inOffsets0[0];
-  outStops[0].color = inColors0 * vec4(inColors0.aaa, 1.0);
-  outStops[1].offset = inOffsets0[1];
-  outStops[1].color = inColors1 * vec4(inColors1.aaa, 1.0);
-  outStops[2].offset = inOffsets0[2];
-  outStops[2].color = inColors2 * vec4(inColors2.aaa, 1.0);
-  outStops[3].offset = inOffsets0[3];
-  outStops[3].color = inColors3 * vec4(inColors3.aaa, 1.0);
-  outStops[4].offset = inOffsets1[0];
-  outStops[4].color = inColors4 * vec4(inColors4.aaa, 1.0);
-  outStops[5].offset = inOffsets1[1];
-  outStops[5].color = inColors5 * vec4(inColors5.aaa, 1.0);
-  outStops[6].offset = inOffsets1[2];
-  outStops[6].color = inColors6 * vec4(inColors6.aaa, 1.0);
-  outStops[7].offset = inOffsets1[3];
-  outStops[7].color = inColors7 * vec4(inColors7.aaa, 1.0);
 }