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.
This commit is contained in:
Benjamin Otte 2023-06-08 12:03:48 +02:00
parent d1135f9e3c
commit e3cc3f7841
5 changed files with 94 additions and 153 deletions

View File

@ -15,9 +15,8 @@ struct _GskVulkanLinearGradientInstance
float start[2]; float start[2];
float end[2]; float end[2];
gint32 repeating; gint32 repeating;
gint32 offset;
gint32 stop_count; 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) 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, .location = 4,
.binding = 0, .binding = 0,
.format = VK_FORMAT_R32_SINT, .format = VK_FORMAT_R32_SINT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count), .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offset),
}, },
{ {
.location = 5, .location = 5,
.binding = 0, .binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT, .format = VK_FORMAT_R32_SINT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offsets), .offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, stop_count),
},
{
.location = 6,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, offsets) + sizeof (float) * 4,
},
{
.location = 7,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[0]),
},
{
.location = 8,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[1]),
},
{
.location = 9,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[2]),
},
{
.location = 10,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[3]),
},
{
.location = 11,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[4]),
},
{
.location = 12,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[5]),
},
{
.location = 13,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[6]),
},
{
.location = 14,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanLinearGradientInstance, colors[7]),
} }
}; };
static const VkPipelineVertexInputStateCreateInfo info = { static const VkPipelineVertexInputStateCreateInfo info = {
@ -175,17 +120,11 @@ gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GskVulkanLinearGradient
const graphene_point_t *start, const graphene_point_t *start,
const graphene_point_t *end, const graphene_point_t *end,
gboolean repeating, gboolean repeating,
gsize n_stops, gsize gradient_offset,
const GskColorStop *stops) gsize n_stops)
{ {
GskVulkanLinearGradientInstance *instance = (GskVulkanLinearGradientInstance *) data; 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[0] = rect->origin.x + offset->x;
instance->rect[1] = rect->origin.y + offset->y; instance->rect[1] = rect->origin.y + offset->y;
instance->rect[2] = rect->size.width; 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[0] = end->x + offset->x;
instance->end[1] = end->y + offset->y; instance->end[1] = end->y + offset->y;
instance->repeating = repeating; instance->repeating = repeating;
instance->offset = gradient_offset;
instance->stop_count = n_stops; 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 gsize

View File

@ -28,8 +28,8 @@ void gsk_vulkan_linear_gradient_pipeline_collect_vertex_data
const graphene_point_t *start, const graphene_point_t *start,
const graphene_point_t *end, const graphene_point_t *end,
gboolean repeating, gboolean repeating,
gsize n_stops, gsize gradient_offset,
const GskColorStop *stops); gsize n_stops);
gsize gsk_vulkan_linear_gradient_pipeline_draw (GskVulkanLinearGradientPipeline*pipeline, gsize gsk_vulkan_linear_gradient_pipeline_draw (GskVulkanLinearGradientPipeline*pipeline,
VkCommandBuffer command_buffer, VkCommandBuffer command_buffer,
gsize offset, gsize offset,

View File

@ -77,6 +77,7 @@ struct _GskVulkanOpRender
gsize vertex_offset; /* offset into vertex buffer */ gsize vertex_offset; /* offset into vertex buffer */
guint32 image_descriptor[2]; /* index into descriptor for the (image, sampler) */ guint32 image_descriptor[2]; /* index into descriptor for the (image, sampler) */
guint32 image_descriptor2[2]; /* index into descriptor for the 2nd image (if relevant) */ 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 source_rect; /* area that source maps to */
graphene_rect_t source2_rect; /* area that source2 maps to */ graphene_rect_t source2_rect; /* area that source2 maps to */
}; };
@ -406,7 +407,7 @@ gsk_vulkan_render_pass_add_color_node (GskVulkanRenderPass *self,
} }
static inline gboolean static inline gboolean
gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass *self, gsk_vulkan_render_pass_add_linear_gradient_node (GskVulkanRenderPass *self,
GskVulkanRender *render, GskVulkanRender *render,
const GskVulkanParseState *state, const GskVulkanParseState *state,
GskRenderNode *node) GskRenderNode *node)
@ -418,11 +419,6 @@ gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass
.render.offset = state->offset, .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)) if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT; pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT) 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_CONTAINER_NODE] = gsk_vulkan_render_pass_add_container_node,
[GSK_CAIRO_NODE] = gsk_vulkan_render_pass_add_cairo_node, [GSK_CAIRO_NODE] = gsk_vulkan_render_pass_add_cairo_node,
[GSK_COLOR_NODE] = gsk_vulkan_render_pass_add_color_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_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
[GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_linear_gradient_node, [GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
[GSK_RADIAL_GRADIENT_NODE] = NULL, [GSK_RADIAL_GRADIENT_NODE] = NULL,
[GSK_REPEATING_RADIAL_GRADIENT_NODE] = NULL, [GSK_REPEATING_RADIAL_GRADIENT_NODE] = NULL,
[GSK_CONIC_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_start (op->render.node),
gsk_linear_gradient_node_get_end (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_render_node_get_node_type (op->render.node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
gsk_linear_gradient_node_get_n_color_stops (op->render.node), op->render.buffer_offset,
gsk_linear_gradient_node_get_color_stops (op->render.node, NULL)); gsk_linear_gradient_node_get_n_color_stops (op->render.node));
break; break;
case GSK_VULKAN_OP_OPACITY: case GSK_VULKAN_OP_OPACITY:
@ -2125,11 +2121,25 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
} }
break; 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: default:
g_assert_not_reached (); g_assert_not_reached ();
case GSK_VULKAN_OP_COLOR: case GSK_VULKAN_OP_COLOR:
case GSK_VULKAN_OP_LINEAR_GRADIENT:
case GSK_VULKAN_OP_BORDER: case GSK_VULKAN_OP_BORDER:
case GSK_VULKAN_OP_INSET_SHADOW: case GSK_VULKAN_OP_INSET_SHADOW:
case GSK_VULKAN_OP_OUTSET_SHADOW: case GSK_VULKAN_OP_OUTSET_SHADOW:

View File

@ -1,6 +1,8 @@
#version 450 #version 450
#include "common.frag.glsl"
#include "clip.frag.glsl" #include "clip.frag.glsl"
#include "rect.frag.glsl"
struct ColorStop { struct ColorStop {
float offset; float offset;
@ -8,29 +10,55 @@ struct ColorStop {
}; };
layout(location = 0) in vec2 inPos; layout(location = 0) in vec2 inPos;
layout(location = 1) in float inGradientPos; layout(location = 1) in flat Rect inRect;
layout(location = 2) in flat int inRepeating; layout(location = 2) in float inGradientPos;
layout(location = 3) in flat int inStopCount; layout(location = 3) in flat int inRepeating;
layout(location = 4) in flat ColorStop inStops[8]; 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() void main()
{ {
float pos; float pos;
if (inRepeating != 0) if (inRepeating != 0)
pos = fract (inGradientPos); pos = fract (inGradientPos);
else else
pos = clamp (inGradientPos, 0, 1); pos = clamp (inGradientPos, 0, 1);
vec4 color = inStops[0].color; ColorStop stop = get_stop (0);
int n = clamp (inStopCount, 2, 8); float last_offset = stop.offset;
for (int i = 1; i < n; i++) color = stop.color;
for (int i = 1; i < inStopCount; i++)
{ {
if (inStops[i].offset > inStops[i-1].offset) stop = get_stop(i);
color = mix (color, inStops[i].color, clamp((pos - inStops[i-1].offset) / (inStops[i].offset - inStops[i-1].offset), 0, 1)); 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); if (last_offset < pos)
outColor = clip (inPos, color); 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);
} }

View File

@ -1,6 +1,7 @@
#version 450 #version 450
#include "clip.vert.glsl" #include "common.vert.glsl"
#include "rect.vert.glsl"
struct ColorStop { struct ColorStop {
float offset; float offset;
@ -11,62 +12,32 @@ layout(location = 0) in vec4 inRect;
layout(location = 1) in vec2 inStart; layout(location = 1) in vec2 inStart;
layout(location = 2) in vec2 inEnd; layout(location = 2) in vec2 inEnd;
layout(location = 3) in int inRepeating; layout(location = 3) in int inRepeating;
layout(location = 4) in int inStopCount; layout(location = 4) in int inStopOffset;
layout(location = 5) in vec4 inOffsets0; layout(location = 5) in int inStopCount;
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 = 0) out vec2 outPos; layout(location = 0) out vec2 outPos;
layout(location = 1) out float outGradientPos; layout(location = 1) out flat Rect outRect;
layout(location = 2) out flat int outRepeating; layout(location = 2) out float outGradientPos;
layout(location = 3) out flat int outStopCount; layout(location = 3) out flat int outRepeating;
layout(location = 4) out flat ColorStop outStops[8]; layout(location = 4) out flat int outStopOffset;
layout(location = 5) out flat int outStopCount;
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) };
float float
get_gradient_pos (vec2 pos) get_gradient_pos (vec2 pos)
{ {
pos = pos - inStart; pos = pos - inStart * push.scale;
vec2 grad = inEnd - inStart; vec2 grad = (inEnd - inStart) * push.scale;
return dot (pos, grad) / dot (grad, grad); return dot (pos, grad) / dot (grad, grad);
} }
void main() { void main() {
vec4 rect = clip (inRect); Rect r = rect_from_gsk (inRect);
vec2 pos = rect.xy + rect.zw * offsets[gl_VertexIndex]; vec2 pos = set_position_from_rect (r);
gl_Position = push.mvp * vec4 (push.scale * pos, 0.0, 1.0);
outPos = pos; outPos = pos;
outRect = r;
outGradientPos = get_gradient_pos (pos); outGradientPos = get_gradient_pos (pos);
outRepeating = inRepeating; outRepeating = inRepeating;
outStopOffset = inStopOffset;
outStopCount = inStopCount; 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);
} }