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 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

View 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,

View 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:

View 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);
}

View 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);
}