From 73f1dfc762e0a447b78388c75dfab53deccee5b3 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 4 Jul 2023 01:55:17 +0200 Subject: [PATCH] vulkan: Repurpose mask shader Use if for mask nodes to do the generic source image + mask image operation with the 4 available mask modes. --- gsk/meson.build | 1 + gsk/vulkan/gskvulkanmaskop.c | 140 ++++++++++++++++++++++++ gsk/vulkan/gskvulkanmaskopprivate.h | 20 ++++ gsk/vulkan/gskvulkanrenderpass.c | 25 ++++- gsk/vulkan/resources/generate-header.py | 2 +- gsk/vulkan/resources/mask.frag | 36 +++++- gsk/vulkan/resources/mask.vert | 59 +++++----- gsk/vulkan/resources/rect.glsl | 6 + 8 files changed, 254 insertions(+), 35 deletions(-) create mode 100644 gsk/vulkan/gskvulkanmaskop.c create mode 100644 gsk/vulkan/gskvulkanmaskopprivate.h diff --git a/gsk/meson.build b/gsk/meson.build index 5743b52c49..707851897c 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -121,6 +121,7 @@ if have_vulkan 'vulkan/gskvulkanimage.c', 'vulkan/gskvulkaninsetshadowop.c', 'vulkan/gskvulkanlineargradientop.c', + 'vulkan/gskvulkanmaskop.c', 'vulkan/gskvulkanmemory.c', 'vulkan/gskvulkanoffscreenop.c', 'vulkan/gskvulkanop.c', diff --git a/gsk/vulkan/gskvulkanmaskop.c b/gsk/vulkan/gskvulkanmaskop.c new file mode 100644 index 0000000000..c6311dc038 --- /dev/null +++ b/gsk/vulkan/gskvulkanmaskop.c @@ -0,0 +1,140 @@ +#include "config.h" + +#include "gskvulkanmaskopprivate.h" + +#include "gskvulkanprivate.h" + +#include "vulkan/resources/mask.vert.h" + +typedef struct _GskVulkanMaskOp GskVulkanMaskOp; + +struct _GskVulkanMaskOp +{ + GskVulkanOp op; + + struct { + GskVulkanImage *image; + graphene_rect_t rect; + graphene_rect_t tex_rect; + guint32 image_descriptor; + } source, mask; + GskMaskMode mask_mode; + + gsize vertex_offset; +}; + +static void +gsk_vulkan_mask_op_finish (GskVulkanOp *op) +{ + GskVulkanMaskOp *self = (GskVulkanMaskOp *) op; + + g_object_unref (self->source.image); + g_object_unref (self->mask.image); +} + +static void +gsk_vulkan_mask_op_upload (GskVulkanOp *op, + GskVulkanRenderPass *pass, + GskVulkanRender *render, + GskVulkanUploader *uploader) +{ +} + +static inline gsize +round_up (gsize number, gsize divisor) +{ + return (number + divisor - 1) / divisor * divisor; +} + +static gsize +gsk_vulkan_mask_op_count_vertex_data (GskVulkanOp *op, + gsize n_bytes) +{ + GskVulkanMaskOp *self = (GskVulkanMaskOp *) op; + gsize vertex_stride; + + vertex_stride = gsk_vulkan_mask_info.pVertexBindingDescriptions[0].stride; + n_bytes = round_up (n_bytes, vertex_stride); + self->vertex_offset = n_bytes; + n_bytes += vertex_stride; + return n_bytes; +} + +static void +gsk_vulkan_mask_op_collect_vertex_data (GskVulkanOp *op, + GskVulkanRenderPass *pass, + GskVulkanRender *render, + guchar *data) +{ + GskVulkanMaskOp *self = (GskVulkanMaskOp *) op; + GskVulkanMaskInstance *instance = (GskVulkanMaskInstance *) (data + self->vertex_offset); + + gsk_vulkan_rect_to_float (&self->source.rect, instance->source_rect); + gsk_vulkan_rect_to_float (&self->source.tex_rect, instance->source_tex_rect); + instance->source_id = self->source.image_descriptor; + gsk_vulkan_rect_to_float (&self->mask.rect, instance->mask_rect); + gsk_vulkan_rect_to_float (&self->mask.tex_rect, instance->mask_tex_rect); + instance->mask_id = self->mask.image_descriptor; + instance->mask_mode = self->mask_mode; +} + +static void +gsk_vulkan_mask_op_reserve_descriptor_sets (GskVulkanOp *op, + GskVulkanRender *render) +{ + GskVulkanMaskOp *self = (GskVulkanMaskOp *) op; + + self->source.image_descriptor = gsk_vulkan_render_get_image_descriptor (render, self->source.image, GSK_VULKAN_SAMPLER_DEFAULT); + self->mask.image_descriptor = gsk_vulkan_render_get_image_descriptor (render, self->mask.image, GSK_VULKAN_SAMPLER_DEFAULT); +} + +static void +gsk_vulkan_mask_op_command (GskVulkanOp *op, + GskVulkanRender *render, + VkPipelineLayout pipeline_layout, + VkCommandBuffer command_buffer) +{ + GskVulkanMaskOp *self = (GskVulkanMaskOp *) op; + + vkCmdDraw (command_buffer, + 6, 1, + 0, self->vertex_offset / gsk_vulkan_mask_info.pVertexBindingDescriptions[0].stride); +} + +static const GskVulkanOpClass GSK_VULKAN_COLOR_MASK_OP_CLASS = { + GSK_VULKAN_OP_SIZE (GskVulkanMaskOp), + "mask", + &gsk_vulkan_mask_info, + gsk_vulkan_mask_op_finish, + gsk_vulkan_mask_op_upload, + gsk_vulkan_mask_op_count_vertex_data, + gsk_vulkan_mask_op_collect_vertex_data, + gsk_vulkan_mask_op_reserve_descriptor_sets, + gsk_vulkan_mask_op_command +}; + +void +gsk_vulkan_mask_op (GskVulkanRenderPass *render_pass, + const char *clip_type, + const graphene_point_t *offset, + GskVulkanImage *source, + const graphene_rect_t *source_rect, + const graphene_rect_t *source_tex_rect, + GskVulkanImage *mask, + const graphene_rect_t *mask_rect, + const graphene_rect_t *mask_tex_rect, + GskMaskMode mask_mode) +{ + GskVulkanMaskOp *self; + + self = (GskVulkanMaskOp *) gsk_vulkan_op_alloc (render_pass, &GSK_VULKAN_COLOR_MASK_OP_CLASS); + + ((GskVulkanOp *) self)->clip_type = g_intern_string (clip_type); + self->source.image = g_object_ref (source); + graphene_rect_offset_r (source_rect, offset->x, offset->y, &self->source.rect); + gsk_vulkan_normalize_tex_coords (&self->source.tex_rect, source_rect, source_tex_rect); + self->mask.image = g_object_ref (mask); + graphene_rect_offset_r (mask_rect, offset->x, offset->y, &self->mask.rect); + gsk_vulkan_normalize_tex_coords (&self->mask.tex_rect, mask_rect, mask_tex_rect); + self->mask_mode = mask_mode; +} diff --git a/gsk/vulkan/gskvulkanmaskopprivate.h b/gsk/vulkan/gskvulkanmaskopprivate.h new file mode 100644 index 0000000000..cc98fc4093 --- /dev/null +++ b/gsk/vulkan/gskvulkanmaskopprivate.h @@ -0,0 +1,20 @@ +#pragma once + +#include "gskvulkanopprivate.h" + +G_BEGIN_DECLS + +void gsk_vulkan_mask_op (GskVulkanRenderPass *render_pass, + const char *clip_type, + const graphene_point_t *offset, + GskVulkanImage *source, + const graphene_rect_t *source_rect, + const graphene_rect_t *source_tex_rect, + GskVulkanImage *mask, + const graphene_rect_t *mask_rect, + const graphene_rect_t *mask_tex_rect, + GskMaskMode mask_mode); + + +G_END_DECLS + diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index 134d38584f..b1e55b2f6a 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -19,6 +19,7 @@ #include "gskvulkanglyphopprivate.h" #include "gskvulkaninsetshadowopprivate.h" #include "gskvulkanlineargradientopprivate.h" +#include "gskvulkanmaskopprivate.h" #include "gskvulkanopprivate.h" #include "gskvulkanprivate.h" #include "gskvulkanrendererprivate.h" @@ -1212,8 +1213,8 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self, const GskVulkanParseState *state, GskRenderNode *node) { - GskVulkanImage *mask_image; - graphene_rect_t mask_tex_rect; + GskVulkanImage *source_image, *mask_image; + graphene_rect_t source_tex_rect, mask_tex_rect; GskRenderNode *source, *mask; GskMaskMode mode; @@ -1249,7 +1250,25 @@ gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self, return TRUE; } - return FALSE; + source_image = gsk_vulkan_render_pass_get_node_as_image (self, + render, + state, + source, + &source_tex_rect); + if (source_image == NULL) + return TRUE; + + gsk_vulkan_mask_op (self, + gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds), + &state->offset, + source_image, + &source->bounds, + &source_tex_rect, + mask_image, + &mask->bounds, + &mask_tex_rect, + mode); + return TRUE; } static inline gboolean diff --git a/gsk/vulkan/resources/generate-header.py b/gsk/vulkan/resources/generate-header.py index 49e7c1e0c0..bcb97117ec 100644 --- a/gsk/vulkan/resources/generate-header.py +++ b/gsk/vulkan/resources/generate-header.py @@ -13,7 +13,7 @@ with open(sys.argv[1]) as f: matches = [] for line in lines: - match = re.search(r"^layout\(location = ([0-9]+)\) in ([a-z0-9]+) ([a-zA-Z0-9]+);$", line) + match = re.search(r"^layout\(location = ([0-9]+)\) in ([a-z0-9]+) ([a-zA-Z0-9_]+);$", line) if not match: if re.search(r"layout.*\sin\s.*", line): raise Exception("Failed to parse file") diff --git a/gsk/vulkan/resources/mask.frag b/gsk/vulkan/resources/mask.frag index db4df18e04..8f80f2f821 100644 --- a/gsk/vulkan/resources/mask.frag +++ b/gsk/vulkan/resources/mask.frag @@ -2,15 +2,41 @@ #include "common.frag.glsl" #include "clip.frag.glsl" +#include "rect.frag.glsl" -layout(location = 0) in vec2 inPos; -layout(location = 1) in vec2 inTexCoord; -layout(location = 2) in vec4 inColor; -layout(location = 3) flat in uint inTexId; +layout(location = 0) in vec2 in_pos; +layout(location = 1) in flat Rect in_source_rect; +layout(location = 2) in vec2 in_source_coord; +layout(location = 3) in flat uint in_source_id; +layout(location = 4) in flat Rect in_mask_rect; +layout(location = 5) in vec2 in_mask_coord; +layout(location = 6) in flat uint in_mask_id; +layout(location = 7) in flat uint in_mask_mode; layout(location = 0) out vec4 color; +float +luminance (vec3 color) +{ + return dot (vec3 (0.2126, 0.7152, 0.0722), color); +} + void main() { - color = clip (inPos, vec4(inColor.rgb * inColor.a, inColor.a) * texture(get_sampler (inTexId), inTexCoord).a); + vec4 source = texture(get_sampler (in_source_id), in_source_coord); + source *= rect_coverage (in_source_rect, in_pos); + vec4 mask = texture(get_sampler (in_mask_id), in_mask_coord); + mask *= rect_coverage (in_mask_rect, in_pos); + + float alpha; + if (in_mask_mode == 0) + alpha = mask.a; + else if (in_mask_mode == 1) + alpha = 1.0 - mask.a; + else if (in_mask_mode == 2) + alpha = luminance (mask.rgb); + else if (in_mask_mode == 3) + alpha = mask.a - luminance (mask.rgb); + + color = clip_scaled (in_pos, source * alpha); } diff --git a/gsk/vulkan/resources/mask.vert b/gsk/vulkan/resources/mask.vert index f787593993..b4b9e95690 100644 --- a/gsk/vulkan/resources/mask.vert +++ b/gsk/vulkan/resources/mask.vert @@ -1,36 +1,43 @@ #version 450 -#include "clip.vert.glsl" +#include "common.vert.glsl" +#include "rect.vert.glsl" -layout(location = 0) in vec4 inRect; -layout(location = 1) in vec4 inTexRect; -layout(location = 2) in vec4 inColor; -layout(location = 3) in uint inTexId; +layout(location = 0) in vec4 in_source_rect; +layout(location = 1) in vec4 in_source_tex_rect; +layout(location = 2) in uint in_source_id; +layout(location = 3) in vec4 in_mask_rect; +layout(location = 4) in vec4 in_mask_tex_rect; +layout(location = 5) in uint in_mask_id; +layout(location = 6) in uint in_mask_mode; -layout(location = 0) out vec2 outPos; -layout(location = 1) out vec2 outTexCoord; -layout(location = 2) out flat vec4 outColor; -layout(location = 3) out flat uint outTexId; +layout(location = 0) out vec2 out_pos; +layout(location = 1) out flat Rect out_source_rect; +layout(location = 2) out vec2 out_source_coord; +layout(location = 3) out flat uint out_source_id; +layout(location = 4) out flat Rect out_mask_rect; +layout(location = 5) out vec2 out_mask_coord; +layout(location = 6) out flat uint out_mask_id; +layout(location = 7) out flat uint out_mask_mode; -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) }; 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 sr = rect_from_gsk (in_source_rect); + Rect mr = rect_from_gsk (in_mask_rect); + Rect r; + if (in_mask_mode == 1) + r = rect_union (sr, mr); + else + r = rect_intersect (sr, mr); - outPos = pos; + vec2 pos = set_position_from_rect (r); - vec4 texrect = vec4((rect.xy - inRect.xy) / inRect.zw, - rect.zw / inRect.zw); - texrect = vec4(inTexRect.xy + inTexRect.zw * texrect.xy, - inTexRect.zw * texrect.zw); - outTexCoord = texrect.xy + texrect.zw * offsets[gl_VertexIndex]; - outTexId = inTexId; - outColor = inColor; + out_pos = pos; + out_source_rect = sr; + out_source_coord = scale_tex_coord (pos, sr, in_source_tex_rect); + out_source_id = in_source_id; + out_mask_rect = mr; + out_mask_coord = scale_tex_coord (pos, mr, in_mask_tex_rect); + out_mask_id = in_mask_id; + out_mask_mode = in_mask_mode; } diff --git a/gsk/vulkan/resources/rect.glsl b/gsk/vulkan/resources/rect.glsl index 51a116c412..af909331ff 100644 --- a/gsk/vulkan/resources/rect.glsl +++ b/gsk/vulkan/resources/rect.glsl @@ -55,4 +55,10 @@ rect_intersect (Rect a, Rect b) return Rect(result); } +Rect +rect_union (Rect a, Rect b) +{ + return Rect (vec4 (min (a.bounds.xy, b.bounds.xy), max (a.bounds.zw, b.bounds.zw))); +} + #endif -- 2.30.2