From e3cc3f78417f391f10774b517ac6057980640691 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 8 Jun 2023 12:03:48 +0200 Subject: [PATCH] vulkan: Make gradient shader use buffers 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 | 94 +++---------------- .../gskvulkanlineargradientpipelineprivate.h | 4 +- gsk/vulkan/gskvulkanrenderpass.c | 38 +++++--- gsk/vulkan/resources/linear.frag | 52 +++++++--- gsk/vulkan/resources/linear.vert | 59 +++--------- 5 files changed, 94 insertions(+), 153 deletions(-) diff --git a/gsk/vulkan/gskvulkanlineargradientpipeline.c b/gsk/vulkan/gskvulkanlineargradientpipeline.c index 79de43720c..1a790de475 100644 --- a/gsk/vulkan/gskvulkanlineargradientpipeline.c +++ b/gsk/vulkan/gskvulkanlineargradientpipeline.c @@ -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 diff --git a/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h b/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h index f33448a91f..21bf10066f 100644 --- a/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h +++ b/gsk/vulkan/gskvulkanlineargradientpipelineprivate.h @@ -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, diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index cacbfd3e4c..67338b74cd 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -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: diff --git a/gsk/vulkan/resources/linear.frag b/gsk/vulkan/resources/linear.frag index cc62e58aac..d23543085f 100644 --- a/gsk/vulkan/resources/linear.frag +++ b/gsk/vulkan/resources/linear.frag @@ -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); } diff --git a/gsk/vulkan/resources/linear.vert b/gsk/vulkan/resources/linear.vert index c0271f2ebc..d2cb76fed9 100644 --- a/gsk/vulkan/resources/linear.vert +++ b/gsk/vulkan/resources/linear.vert @@ -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); }