vulkan: Repurpose mask shader
authorBenjamin Otte <otte@redhat.com>
Mon, 3 Jul 2023 23:55:17 +0000 (01:55 +0200)
committerBenjamin Otte <otte@redhat.com>
Sun, 16 Jul 2023 10:13:00 +0000 (12:13 +0200)
Use if for mask nodes to do the generic source image + mask image
operation with the 4 available mask modes.

gsk/meson.build
gsk/vulkan/gskvulkanmaskop.c [new file with mode: 0644]
gsk/vulkan/gskvulkanmaskopprivate.h [new file with mode: 0644]
gsk/vulkan/gskvulkanrenderpass.c
gsk/vulkan/resources/generate-header.py
gsk/vulkan/resources/mask.frag
gsk/vulkan/resources/mask.vert
gsk/vulkan/resources/rect.glsl

index 5743b52c497662611039aa7acc416a6eef48963e..707851897c39f55552bfd317c5ca4b51aefeb0e6 100644 (file)
@@ -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 (file)
index 0000000..c6311dc
--- /dev/null
@@ -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 (file)
index 0000000..cc98fc4
--- /dev/null
@@ -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
+
index 134d38584fc0684a5e6620f4d3bcd1708ebf698e..b1e55b2f6a0c54762f8708c9e55bff27cdd2ab09 100644 (file)
@@ -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
index 49e7c1e0c000c02a6cc248677efbdde340ed6769..bcb97117ec69be05ff0734fd5fb7832ae4b28df6 100644 (file)
@@ -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")
index db4df18e04f3abeaccda7386c59d76f09588a1c6..8f80f2f8213ee23ae11a8e37481a8ec3ab025d0e 100644 (file)
@@ -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);
 }
index f7875939933d6303a735ea9a2eb3d8efb3672c12..b4b9e956901fd36244718e360933b32680095722 100644 (file)
@@ -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;
 }
index 51a116c412afa433cbed267d5ebb171571b6e3c3..af909331ff8fb44e90c9c149f78c75ed966bab15 100644 (file)
@@ -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