From ec5aa5e7360b6e19d4a87a95aa2224c619510f58 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 23 Dec 2023 23:43:06 +0100 Subject: [PATCH] gpu: Add a mask shader This shader can take over from the ubershader. And it can be used instead of launching the ubershader when no offscreens are necessary. Also includes an optimization that uses the colorize shader when appropriate. --- gsk/gpu/gskgpumaskop.c | 83 ++++++++++++++++++++++++++++ gsk/gpu/gskgpumaskopprivate.h | 23 ++++++++ gsk/gpu/gskgpunodeprocessor.c | 97 ++++++++++++++++++++++++++++++++- gsk/gpu/shaders/enums.glsl | 5 ++ gsk/gpu/shaders/gskgpumask.glsl | 86 +++++++++++++++++++++++++++++ gsk/gpu/shaders/meson.build | 1 + gsk/meson.build | 1 + 7 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 gsk/gpu/gskgpumaskop.c create mode 100644 gsk/gpu/gskgpumaskopprivate.h create mode 100644 gsk/gpu/shaders/gskgpumask.glsl diff --git a/gsk/gpu/gskgpumaskop.c b/gsk/gpu/gskgpumaskop.c new file mode 100644 index 0000000000..2f15e5a310 --- /dev/null +++ b/gsk/gpu/gskgpumaskop.c @@ -0,0 +1,83 @@ +#include "config.h" + +#include "gskgpumaskopprivate.h" + +#include "gskgpuframeprivate.h" +#include "gskgpuprintprivate.h" +#include "gskrectprivate.h" + +#include "gpu/shaders/gskgpumaskinstance.h" + +typedef struct _GskGpuMaskOp GskGpuMaskOp; + +struct _GskGpuMaskOp +{ + GskGpuShaderOp op; +}; + +static void +gsk_gpu_mask_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) +{ + GskGpuShaderOp *shader = (GskGpuShaderOp *) op; + GskGpuMaskInstance *instance; + + instance = (GskGpuMaskInstance *) gsk_gpu_frame_get_vertex_data (frame, shader->vertex_offset); + + gsk_gpu_print_op (string, indent, "mask"); + gsk_gpu_print_rect (string, instance->rect); + gsk_gpu_print_image_descriptor (string, shader->desc, instance->source_id); + gsk_gpu_print_image_descriptor (string, shader->desc, instance->mask_id); + gsk_gpu_print_newline (string); +} + +static const GskGpuShaderOpClass GSK_GPU_MASK_OP_CLASS = { + { + GSK_GPU_OP_SIZE (GskGpuMaskOp), + GSK_GPU_STAGE_SHADER, + gsk_gpu_shader_op_finish, + gsk_gpu_mask_op_print, +#ifdef GDK_RENDERING_VULKAN + gsk_gpu_shader_op_vk_command, +#endif + gsk_gpu_shader_op_gl_command + }, + "gskgpumask", + sizeof (GskGpuMaskInstance), +#ifdef GDK_RENDERING_VULKAN + &gsk_gpu_mask_info, +#endif + gsk_gpu_mask_setup_vao +}; + +void +gsk_gpu_mask_op (GskGpuFrame *frame, + GskGpuShaderClip clip, + GskGpuDescriptors *desc, + const graphene_rect_t *rect, + const graphene_point_t *offset, + float opacity, + GskMaskMode mask_mode, + guint32 source_descriptor, + const graphene_rect_t *source_rect, + guint32 mask_descriptor, + const graphene_rect_t *mask_rect) +{ + GskGpuMaskInstance *instance; + + gsk_gpu_shader_op_alloc (frame, + &GSK_GPU_MASK_OP_CLASS, + clip, + desc, + &instance); + + gsk_gpu_rect_to_float (rect, offset, instance->rect); + gsk_gpu_rect_to_float (source_rect, offset, instance->source_rect); + instance->source_id = source_descriptor; + gsk_gpu_rect_to_float (mask_rect, offset, instance->mask_rect); + instance->mask_id = mask_descriptor; + instance->mask_mode = mask_mode; + instance->opacity = opacity; +} diff --git a/gsk/gpu/gskgpumaskopprivate.h b/gsk/gpu/gskgpumaskopprivate.h new file mode 100644 index 0000000000..ed321a4b1e --- /dev/null +++ b/gsk/gpu/gskgpumaskopprivate.h @@ -0,0 +1,23 @@ +#pragma once + +#include "gskgpushaderopprivate.h" + +#include + +G_BEGIN_DECLS + +void gsk_gpu_mask_op (GskGpuFrame *frame, + GskGpuShaderClip clip, + GskGpuDescriptors *desc, + const graphene_rect_t *rect, + const graphene_point_t *offset, + float opacity, + GskMaskMode mask_mode, + guint32 source_descriptor, + const graphene_rect_t *source_rect, + guint32 mask_descriptor, + const graphene_rect_t *mask_rect); + + +G_END_DECLS + diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index 48ab6ff59d..e3bdb99a70 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -16,6 +16,7 @@ #include "gskgpuframeprivate.h" #include "gskgpuglobalsopprivate.h" #include "gskgpuimageprivate.h" +#include "gskgpumaskopprivate.h" #include "gskgpumipmapopprivate.h" #include "gskgpurenderpassopprivate.h" #include "gskgpuroundedcoloropprivate.h" @@ -2235,6 +2236,100 @@ gsk_gpu_node_processor_create_cross_fade_pattern (GskGpuPatternWriter *self, return TRUE; } +static void +gsk_gpu_node_processor_add_mask_node (GskGpuNodeProcessor *self, + GskRenderNode *node) +{ + GskRenderNode *source_child, *mask_child; + GskGpuImage *mask_image; + graphene_rect_t bounds, mask_rect; + guint32 mask_descriptor; + GskMaskMode mask_mode; + + source_child = gsk_mask_node_get_source (node); + mask_child = gsk_mask_node_get_mask (node); + mask_mode = gsk_mask_node_get_mask_mode (node); + + if ((gsk_gpu_node_processor_ubershader_instead_of_offscreen (self, mask_child) || + (gsk_gpu_node_processor_ubershader_instead_of_offscreen (self, source_child) && + gsk_render_node_get_node_type (source_child) != GSK_COLOR_NODE)) && + gsk_gpu_node_processor_try_node_as_pattern (self, node)) + return; + + if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &bounds)) + return; + + mask_image = gsk_gpu_node_processor_get_node_as_image (self, + 0, + GSK_GPU_IMAGE_STRAIGHT_ALPHA, + &bounds, + mask_child, + &mask_rect); + if (mask_image == NULL) + { + if (mask_mode == GSK_MASK_MODE_INVERTED_ALPHA) + gsk_gpu_node_processor_add_node (self, source_child); + return; + } + mask_descriptor = gsk_gpu_node_processor_add_image (self, mask_image, GSK_GPU_SAMPLER_DEFAULT); + + if (gsk_render_node_get_node_type (source_child) == GSK_COLOR_NODE && + mask_mode == GSK_MASK_MODE_ALPHA) + { + const GdkRGBA *rgba = gsk_color_node_get_color (source_child); + gsk_gpu_colorize_op (self->frame, + gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds), + self->desc, + mask_descriptor, + &node->bounds, + &self->offset, + &mask_rect, + &GDK_RGBA_INIT_ALPHA (rgba, self->opacity)); + } + else + { + GskGpuDescriptors *desc = self->desc; + GskGpuImage *source_image; + graphene_rect_t source_rect; + guint32 source_descriptor; + + source_image = gsk_gpu_node_processor_get_node_as_image (self, + 0, + GSK_GPU_IMAGE_STRAIGHT_ALPHA, + &bounds, + source_child, + &source_rect); + if (source_image == NULL) + { + g_object_unref (mask_image); + return; + } + source_descriptor = gsk_gpu_node_processor_add_image (self, source_image, GSK_GPU_SAMPLER_DEFAULT); + if (desc != self->desc) + { + desc = self->desc; + mask_descriptor = gsk_gpu_node_processor_add_image (self, mask_image, GSK_GPU_SAMPLER_DEFAULT); + g_assert (desc == self->desc); + } + + gsk_gpu_mask_op (self->frame, + gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds), + desc, + &node->bounds, + &self->offset, + self->opacity, + mask_mode, + source_descriptor, + &source_rect, + mask_descriptor, + &mask_rect); + + g_object_unref (source_image); + } + + g_object_unref (mask_image); +} + static gboolean gsk_gpu_node_processor_create_mask_pattern (GskGpuPatternWriter *self, GskRenderNode *node) @@ -3022,7 +3117,7 @@ static const struct [GSK_MASK_NODE] = { 0, GSK_GPU_HANDLE_OPACITY, - gsk_gpu_node_processor_add_node_as_pattern, + gsk_gpu_node_processor_add_mask_node, gsk_gpu_node_processor_create_mask_pattern, }, [GSK_FILL_NODE] = { diff --git a/gsk/gpu/shaders/enums.glsl b/gsk/gpu/shaders/enums.glsl index 0dce96c564..15bbe4da62 100644 --- a/gsk/gpu/shaders/enums.glsl +++ b/gsk/gpu/shaders/enums.glsl @@ -47,6 +47,11 @@ #define GSK_GPU_PATTERN_BLEND_SATURATION 37u #define GSK_GPU_PATTERN_BLEND_LUMINOSITY 38u +#define GSK_MASK_MODE_ALPHA 0u +#define GSK_MASK_MODE_INVERTED_ALPHA 1u +#define GSK_MASK_MODE_LUMINANCE 2u +#define GSK_MASK_MODE_INVERTED_LUMINANCE 3u + #define TOP 0u #define RIGHT 1u #define BOTTOM 2u diff --git a/gsk/gpu/shaders/gskgpumask.glsl b/gsk/gpu/shaders/gskgpumask.glsl new file mode 100644 index 0000000000..2d0688de02 --- /dev/null +++ b/gsk/gpu/shaders/gskgpumask.glsl @@ -0,0 +1,86 @@ +#include "common.glsl" + +PASS(0) vec2 _pos; +PASS_FLAT(1) Rect _source_rect; +PASS_FLAT(2) Rect _mask_rect; +PASS(3) vec2 _source_coord; +PASS(4) vec2 _mask_coord; +PASS_FLAT(5) uint _source_id; +PASS_FLAT(6) uint _mask_id; +PASS_FLAT(7) uint _mask_mode; +PASS_FLAT(8) float _opacity; + + +#ifdef GSK_VERTEX_SHADER + +IN(0) vec4 in_rect; +IN(1) vec4 in_source_rect; +IN(2) uint in_source_id; +IN(3) vec4 in_mask_rect; +IN(4) uint in_mask_id; +IN(5) uint in_mask_mode; +IN(6) float in_opacity; + +void +run (out vec2 pos) +{ + Rect r = rect_from_gsk (in_rect); + + pos = rect_get_position (r); + + _pos = pos; + Rect source_rect = rect_from_gsk (in_source_rect); + _source_rect = source_rect; + _source_coord = rect_get_coord (source_rect, pos); + _source_id = in_source_id; + Rect mask_rect = rect_from_gsk (in_mask_rect); + _mask_rect = mask_rect; + _mask_coord = rect_get_coord (mask_rect, pos); + _mask_id = in_mask_id; + _mask_mode = in_mask_mode; + _opacity = in_opacity; +} + +#endif + + + +#ifdef GSK_FRAGMENT_SHADER + +void +run (out vec4 color, + out vec2 position) +{ + vec4 source = gsk_texture (_source_id, _source_coord) * + rect_coverage (_source_rect, _pos); + vec4 mask = gsk_texture (_mask_id, _mask_coord) * + rect_coverage (_mask_rect, _pos); + + float alpha = _opacity; + switch (_mask_mode) + { + case GSK_MASK_MODE_ALPHA: + alpha *= mask.a; + break; + + case GSK_MASK_MODE_INVERTED_ALPHA: + alpha *= (1.0 - mask.a); + break; + + case GSK_MASK_MODE_LUMINANCE: + alpha *= luminance (mask.rgb); + break; + + case GSK_MASK_MODE_INVERTED_LUMINANCE: + alpha *= (mask.a - luminance (mask.rgb)); + break; + + default: + break; + } + + color = source * alpha; + position = _pos; +} + +#endif diff --git a/gsk/gpu/shaders/meson.build b/gsk/gpu/shaders/meson.build index 0be1cb5981..de25097949 100644 --- a/gsk/gpu/shaders/meson.build +++ b/gsk/gpu/shaders/meson.build @@ -19,6 +19,7 @@ gsk_private_gpu_shaders = files([ 'gskgpucolor.glsl', 'gskgpucolorize.glsl', 'gskgpucolormatrix.glsl', + 'gskgpumask.glsl', 'gskgpuroundedcolor.glsl', 'gskgpustraightalpha.glsl', 'gskgputexture.glsl', diff --git a/gsk/meson.build b/gsk/meson.build index 39219b40d3..cdff824231 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -88,6 +88,7 @@ gsk_private_sources = files([ 'gpu/gskgpuframe.c', 'gpu/gskgpuglobalsop.c', 'gpu/gskgpuimage.c', + 'gpu/gskgpumaskop.c', 'gpu/gskgpumipmapop.c', 'gpu/gskgpunodeprocessor.c', 'gpu/gskgpuop.c',