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.
This commit is contained in:
Benjamin Otte 2023-12-23 23:43:06 +01:00
parent cb5c994cd9
commit bd901896ee
7 changed files with 295 additions and 1 deletions

83
gsk/gpu/gskgpumaskop.c Normal file
View File

@ -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;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "gskgpushaderopprivate.h"
#include <graphene.h>
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

View File

@ -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] = {

View File

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

View File

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

View File

@ -19,6 +19,7 @@ gsk_private_gpu_shaders = files([
'gskgpucolor.glsl',
'gskgpucolorize.glsl',
'gskgpucolormatrix.glsl',
'gskgpumask.glsl',
'gskgpuroundedcolor.glsl',
'gskgpustraightalpha.glsl',
'gskgputexture.glsl',

View File

@ -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',