Compare commits

...

6 Commits

Author SHA1 Message Date
Matthias Clasen
6f8dc14dc9 gsk: Implement the 'underline trick'
Add an optimization that turns color ops into colorize ops when
the previous op was colorize as well. This is beneficial when we
have a mix of text and color nodes (such as underlined text, or
text with background color).
2024-03-05 09:15:39 -05:00
Matthias Clasen
835adc81a7 gsk: Add a 'colorize' optimization flag
We are going to add an optimization that turns color ops into
colorize ops when beneficial. This flags will make it possible
to turn that off with

GSK_GPU_SKIP=colorize
2024-03-05 09:15:39 -05:00
Matthias Clasen
df6d8a283b gsk: Export the colorize op class
This is going to be used in the future to check whether the previous
op was a colorize one.
2024-03-05 09:15:39 -05:00
Matthias Clasen
112d84b15c gsk: Add a way to get a 'solid' image
Add a method to GskGpuDevice that returns a 1x1 image in the atlas,
for use as a mask.
2024-03-05 09:15:39 -05:00
Matthias Clasen
1c7c9503e6 gsk: Add a solid upload op
Add a variant of the glyph upload op that just draws a 3x3 white
rect, for use as a mask.
2024-03-05 09:15:38 -05:00
Matthias Clasen
11dca18449 gsk: Add gsk_gpu_frame_get_last_op
This function will be used in the future to find the previous
op during node processing, so we can make optimization decisions
based on that.
2024-03-05 09:13:35 -05:00
11 changed files with 269 additions and 14 deletions

View File

@@ -34,7 +34,7 @@ gsk_gpu_colorize_op_print (GskGpuOp *op,
gsk_gpu_print_newline (string);
}
static const GskGpuShaderOpClass GSK_GPU_COLORIZE_OP_CLASS = {
const GskGpuShaderOpClass GSK_GPU_COLORIZE_OP_CLASS = {
{
GSK_GPU_OP_SIZE (GskGpuColorizeOp),
GSK_GPU_STAGE_SHADER,

View File

@@ -6,6 +6,8 @@
G_BEGIN_DECLS
extern const GskGpuShaderOpClass GSK_GPU_COLORIZE_OP_CLASS;
void gsk_gpu_colorize_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GskGpuDescriptors *desc,

View File

@@ -179,6 +179,10 @@ struct _GskGpuCachedAtlas
GskGpuImage *image;
gboolean has_colorize;
gsize colorize_x;
gsize colorize_y;
gsize n_slices;
struct {
gsize width;
@@ -233,6 +237,8 @@ gsk_gpu_cached_atlas_new (GskGpuDevice *device)
self = gsk_gpu_cached_new (device, &GSK_GPU_CACHED_ATLAS_CLASS, NULL);
self->image = GSK_GPU_DEVICE_GET_CLASS (device)->create_atlas_image (device, ATLAS_SIZE, ATLAS_SIZE);
self->has_colorize = FALSE;
return self;
}
@@ -981,5 +987,43 @@ gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
return cache->image;
}
GskGpuImage *
gsk_gpu_device_get_solid_image (GskGpuDevice *self,
GskGpuFrame *frame,
graphene_rect_t *out_bounds)
{
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
gsk_gpu_device_ensure_atlas (self, FALSE);
if (!priv->current_atlas->has_colorize)
{
gsize x, y;
if (!gsk_gpu_cached_atlas_allocate (priv->current_atlas, 5, 5, &x, &y))
{
gsk_gpu_device_ensure_atlas (self, TRUE);
gsk_gpu_cached_atlas_allocate (priv->current_atlas, 5, 5, &x, &y);
}
gsk_gpu_upload_solid_op (frame,
priv->current_atlas->image,
&(cairo_rectangle_int_t) { x, y, 5, 5 },
&GRAPHENE_POINT_INIT (1, 1));
priv->current_atlas->colorize_x = x + 2;
priv->current_atlas->colorize_y = y + 2;
priv->current_atlas->has_colorize = TRUE;
}
out_bounds->origin.x = priv->current_atlas->colorize_x;
out_bounds->origin.y = priv->current_atlas->colorize_y;
out_bounds->size.width = 1;
out_bounds->size.height = 1;
return priv->current_atlas->image;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -98,6 +98,9 @@ GskGpuImage * gsk_gpu_device_lookup_glyph_image (GskGpuD
graphene_rect_t *out_bounds,
graphene_point_t *out_origin);
GskGpuImage * gsk_gpu_device_get_solid_image (GskGpuDevice *self,
GskGpuFrame *frame,
graphene_rect_t *out_bounds);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuDevice, g_object_unref)

View File

@@ -40,6 +40,7 @@ struct _GskGpuFramePrivate
GskGpuOps ops;
GskGpuOp *first_op;
GskGpuOp *last_op;
GskGpuBuffer *vertex_buffer;
guchar *vertex_buffer_data;
@@ -70,6 +71,8 @@ gsk_gpu_frame_default_cleanup (GskGpuFrame *self)
gsk_gpu_op_finish (op);
}
gsk_gpu_ops_set_size (&priv->ops, 0);
priv->last_op = NULL;
}
static void
@@ -331,7 +334,7 @@ gsk_gpu_frame_sort_ops (GskGpuFrame *self)
{
GskGpuFramePrivate *priv = gsk_gpu_frame_get_instance_private (self);
SortData sort_data = { { NULL, }, };
gsk_gpu_frame_sort_render_pass (self, priv->first_op, &sort_data);
if (sort_data.upload.first)
@@ -343,6 +346,8 @@ gsk_gpu_frame_sort_ops (GskGpuFrame *self)
priv->first_op = sort_data.command.first;
if (sort_data.command.last)
sort_data.command.last->next = NULL;
priv->last_op = NULL;
}
gpointer
@@ -360,7 +365,17 @@ gsk_gpu_frame_alloc_op (GskGpuFrame *self,
NULL,
size);
return gsk_gpu_ops_index (&priv->ops, pos);
priv->last_op = (GskGpuOp *) gsk_gpu_ops_index (&priv->ops, pos);
return priv->last_op;
}
GskGpuOp *
gsk_gpu_frame_get_last_op (GskGpuFrame *self)
{
GskGpuFramePrivate *priv = gsk_gpu_frame_get_instance_private (self);
return priv->last_op;
}
GskGpuImage *

View File

@@ -83,6 +83,7 @@ void gsk_gpu_frame_download_texture (GskGpuF
GdkMemoryFormat format,
guchar *data,
gsize stride);
GskGpuOp *gsk_gpu_frame_get_last_op (GskGpuFrame *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuFrame, g_object_unref)

View File

@@ -1775,6 +1775,57 @@ gsk_gpu_node_processor_add_opacity_node (GskGpuNodeProcessor *self,
self->opacity = old_opacity;
}
static void
gsk_gpu_node_processor_color_op (GskGpuNodeProcessor *self,
GskGpuShaderClip clip,
const graphene_rect_t *rect,
const graphene_point_t *offset,
const GdkRGBA *color)
{
GskGpuOp *last_op = gsk_gpu_frame_get_last_op (self->frame);
if (last_op &&
last_op->op_class == (const GskGpuOpClass *) &GSK_GPU_COLORIZE_OP_CLASS &&
gsk_gpu_frame_should_optimize (self->frame, GSK_GPU_OPTIMIZE_COLORIZE))
{
GskGpuDevice *device;
GskGpuImage *image;
guint32 descriptor;
graphene_rect_t bounds, tex_rect;
device = gsk_gpu_frame_get_device (self->frame);
image = gsk_gpu_device_get_solid_image (device, self->frame, &bounds);
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);
gsk_rect_scale (&GRAPHENE_RECT_INIT (- bounds.origin.x,
- bounds.origin.y,
gsk_gpu_image_get_width (image),
gsk_gpu_image_get_height (image)),
rect->size.width / bounds.size.width,
rect->size.height / bounds.size.height,
&tex_rect);
gsk_rect_init_offset (&tex_rect, &tex_rect, rect->origin.x, rect->origin.y);
gsk_gpu_colorize_op (self->frame,
clip,
self->desc,
descriptor,
rect,
offset,
&tex_rect,
color);
}
else
{
gsk_gpu_color_op (self->frame,
clip,
rect,
offset,
color);
}
}
static void
gsk_gpu_node_processor_add_color_node (GskGpuNodeProcessor *self,
GskRenderNode *node)
@@ -1809,11 +1860,11 @@ gsk_gpu_node_processor_add_color_node (GskGpuNodeProcessor *self,
if (self->modelview)
{
/* Yuck, rounded clip and modelview. I give up. */
gsk_gpu_color_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
&node->bounds,
&self->offset,
gsk_color_node_get_color (node));
gsk_gpu_node_processor_color_op (self,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
&node->bounds,
&self->offset,
gsk_color_node_get_color (node));
return;
}
@@ -1879,11 +1930,11 @@ gsk_gpu_node_processor_add_color_node (GskGpuNodeProcessor *self,
return;
}
gsk_gpu_color_op (self->frame,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
&node->bounds,
&self->offset,
&GDK_RGBA_INIT_ALPHA (color, self->opacity));
gsk_gpu_node_processor_color_op (self,
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
&node->bounds,
&self->offset,
&GDK_RGBA_INIT_ALPHA (color, self->opacity));
}
static gboolean

View File

@@ -31,7 +31,9 @@ static const GdkDebugKey gsk_gpu_optimization_keys[] = {
{ "gradients", GSK_GPU_OPTIMIZE_GRADIENTS, "Don't supersample gradients" },
{ "mipmap", GSK_GPU_OPTIMIZE_MIPMAP, "Avoid creating mipmaps" },
{ "glyph-align", GSK_GPU_OPTIMIZE_GLYPH_ALIGN, "Never align glyphs to the subpixel grid" },
{ "colorize", GSK_GPU_OPTIMIZE_COLORIZE, "Don't replace color by colorize" },
/* These require hardware support */
{ "gl-baseinstance", GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE, "Assume no ARB/EXT_base_instance support" },
};

View File

@@ -119,7 +119,9 @@ typedef enum {
GSK_GPU_OPTIMIZE_GRADIENTS = 1 << 4,
GSK_GPU_OPTIMIZE_MIPMAP = 1 << 5,
GSK_GPU_OPTIMIZE_GLYPH_ALIGN = 1 << 6,
GSK_GPU_OPTIMIZE_COLORIZE = 1 << 7,
/* These require hardware support */
GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE = 1 << 7,
GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE = 1 << 16,
} GskGpuOptimizations;

View File

@@ -636,3 +636,134 @@ gsk_gpu_upload_glyph_op (GskGpuFrame *frame,
self->glyph = glyph;
self->origin = *origin;
}
typedef struct _GskGpuUploadSolidOp GskGpuUploadSolidOp;
struct _GskGpuUploadSolidOp
{
GskGpuOp op;
GskGpuImage *image;
cairo_rectangle_int_t area;
graphene_point_t origin;
GskGpuBuffer *buffer;
};
static void
gsk_gpu_upload_solid_op_finish (GskGpuOp *op)
{
GskGpuUploadSolidOp *self = (GskGpuUploadSolidOp *) op;
g_object_unref (self->image);
g_clear_object (&self->buffer);
}
static void
gsk_gpu_upload_solid_op_print (GskGpuOp *op,
GskGpuFrame *frame,
GString *string,
guint indent)
{
GskGpuUploadSolidOp *self = (GskGpuUploadSolidOp *) op;
gsk_gpu_print_op (string, indent, "upload-solid");
gsk_gpu_print_int_rect (string, &self->area);
gsk_gpu_print_newline (string);
}
static void
gsk_gpu_upload_solid_op_draw (GskGpuOp *op,
guchar *data,
gsize stride)
{
GskGpuUploadSolidOp *self = (GskGpuUploadSolidOp *) op;
cairo_surface_t *surface;
cairo_t *cr;
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
self->area.width,
self->area.height,
stride);
cairo_surface_set_device_offset (surface, self->origin.x, self->origin.y);
cr = cairo_create (surface);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
/* Make sure the entire surface is initialized to black */
cairo_set_source_rgba (cr, 0, 0, 0, 0);
cairo_rectangle (cr, 0.0, 0.0, self->area.width, self->area.height);
cairo_fill (cr);
/* Draw solid */
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cairo_rectangle (cr, 0, 0, self->area.width, self->area.height);
cairo_fill (cr);
cairo_destroy (cr);
cairo_surface_finish (surface);
cairo_surface_destroy (surface);
}
#ifdef GDK_RENDERING_VULKAN
static GskGpuOp *
gsk_gpu_upload_solid_op_vk_command (GskGpuOp *op,
GskGpuFrame *frame,
GskVulkanCommandState *state)
{
GskGpuUploadSolidOp *self = (GskGpuUploadSolidOp *) op;
return gsk_gpu_upload_op_vk_command_with_area (op,
frame,
state,
GSK_VULKAN_IMAGE (self->image),
&self->area,
gsk_gpu_upload_solid_op_draw,
&self->buffer);
}
#endif
static GskGpuOp *
gsk_gpu_upload_solid_op_gl_command (GskGpuOp *op,
GskGpuFrame *frame,
GskGLCommandState *state)
{
GskGpuUploadSolidOp *self = (GskGpuUploadSolidOp *) op;
return gsk_gpu_upload_op_gl_command_with_area (op,
frame,
self->image,
&self->area,
gsk_gpu_upload_solid_op_draw);
}
static const GskGpuOpClass GSK_GPU_UPLOAD_SOLID_OP_CLASS = {
GSK_GPU_OP_SIZE (GskGpuUploadSolidOp),
GSK_GPU_STAGE_UPLOAD,
gsk_gpu_upload_solid_op_finish,
gsk_gpu_upload_solid_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_upload_solid_op_vk_command,
#endif
gsk_gpu_upload_solid_op_gl_command,
};
void
gsk_gpu_upload_solid_op (GskGpuFrame *frame,
GskGpuImage *image,
const cairo_rectangle_int_t *area,
const graphene_point_t *origin)
{
GskGpuUploadSolidOp *self;
self = (GskGpuUploadSolidOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_UPLOAD_SOLID_OP_CLASS);
self->image = g_object_ref (image);
self->area = *area;
self->origin = *origin;
}

View File

@@ -27,5 +27,9 @@ void gsk_gpu_upload_glyph_op (GskGpuF
const cairo_rectangle_int_t *area,
const graphene_point_t *origin);
void gsk_gpu_upload_solid_op (GskGpuFrame *frame,
GskGpuImage *image,
const cairo_rectangle_int_t *area,
const graphene_point_t *origin);
G_END_DECLS