diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-12-14 20:35:39 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-12-22 23:51:11 -0500 |
commit | a03a821840962d3f4eb3f91f3a00ee8128ba740d (patch) | |
tree | 432080ac7af0c46a944090bba9cda5d9afaa2789 | |
parent | 4d4993b8ae843ee65a35b844ae84a4b98472810b (diff) | |
download | gtk+-mask-nodes.tar.gz |
gl: Render fill/stroke nodes using the mask shadermask-nodes
Convert the path to an alpha mask and upload that
as a texture to use with the mask shader. We use
the path cache to reuse masks for the same path.
-rw-r--r-- | gsk/gl/gskglpathcache.c | 15 | ||||
-rw-r--r-- | gsk/gl/gskglpathcacheprivate.h | 4 | ||||
-rw-r--r-- | gsk/gl/gskglrenderer.c | 222 | ||||
-rw-r--r-- | gsk/gl/gskglrenderopsprivate.h | 1 |
4 files changed, 239 insertions, 3 deletions
diff --git a/gsk/gl/gskglpathcache.c b/gsk/gl/gskglpathcache.c index 1dcb0c33d8..13f159e7c1 100644 --- a/gsk/gl/gskglpathcache.c +++ b/gsk/gl/gskglpathcache.c @@ -10,6 +10,8 @@ typedef struct GskPath *path; GskFillRule fill_rule; const GskStroke *stroke; + float scale_x; + float scale_y; guint hash; GskStroke stroke_copy; @@ -27,6 +29,8 @@ compute_hash (CacheItem *item) hash += item->fill_rule; if (item->stroke) hash += gsk_stroke_hash (item->stroke); + hash += (int)(item->scale_x * 100); + hash += (int)(item->scale_y * 200); return hash; } @@ -48,6 +52,8 @@ cache_key_equal (gconstpointer v1, return item1->path == item2->path && item1->fill_rule == item2->fill_rule && + item1->scale_x == item2->scale_x && + item1->scale_y == item2->scale_y && (item1->stroke == item2->stroke || (item1->stroke && item2->stroke && gsk_stroke_equal (item1->stroke, item2->stroke))); @@ -102,6 +108,8 @@ gsk_gl_path_cache_get_texture_id (GskGLPathCache *self, GskPath *path, GskFillRule fill_rule, const GskStroke *stroke, + float scale_x, + float scale_y, graphene_rect_t *out_bounds) { CacheItem key; @@ -110,6 +118,8 @@ gsk_gl_path_cache_get_texture_id (GskGLPathCache *self, key.path = path; key.fill_rule = fill_rule; key.stroke = stroke; + key.scale_x = scale_x; + key.scale_y = scale_y; key.hash = compute_hash (&key); item = g_hash_table_lookup (self->textures, &key); @@ -130,6 +140,8 @@ gsk_gl_path_cache_commit (GskGLPathCache *self, GskPath *path, GskFillRule fill_rule, const GskStroke *stroke, + float scale_x, + float scale_y, int texture_id, const graphene_rect_t *bounds) { @@ -147,6 +159,9 @@ gsk_gl_path_cache_commit (GskGLPathCache *self, gsk_stroke_init_copy (&item->stroke_copy, stroke); item->stroke = &item->stroke_copy; } + item->scale_x = scale_x; + item->scale_y = scale_y; + item->hash = compute_hash (item); item->unused_frames = 0; diff --git a/gsk/gl/gskglpathcacheprivate.h b/gsk/gl/gskglpathcacheprivate.h index 181d4be26f..cc2081c8ea 100644 --- a/gsk/gl/gskglpathcacheprivate.h +++ b/gsk/gl/gskglpathcacheprivate.h @@ -20,11 +20,15 @@ int gsk_gl_path_cache_get_texture_id (GskGLPathCache *self, GskPath *path, GskFillRule fill_rule, const GskStroke *stroke, + float scale_x, + float scale_y, graphene_rect_t *out_bounds); void gsk_gl_path_cache_commit (GskGLPathCache *self, GskPath *path, GskFillRule fill_rule, const GskStroke *stroke, + float scale_x, + float scale_y, int texture_id, const graphene_rect_t *bounds); diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 09b7b4f0a1..4b70320b71 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -2764,6 +2764,217 @@ render_mask_node (GskGLRenderer *self, load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); } +static GdkTexture * +make_path_mask (GskPath *path, + GskFillRule fill_rule, + float scale_x, + float scale_y, + graphene_rect_t *bounds) +{ + cairo_surface_t *surface; + cairo_t *cr; + int width; + int height; + int stride; + guchar *buffer; + GBytes *bytes; + GdkTexture *mask; + + gsk_path_get_bounds (path, bounds); + + width = ceilf (bounds->size.width * scale_x); + height = ceilf (bounds->size.height * scale_y); + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); + + buffer = g_malloc0 (stride * height); + surface = cairo_image_surface_create_for_data (buffer, + CAIRO_FORMAT_ARGB32, + width, height, + stride); + cairo_surface_set_device_scale (surface, scale_x, scale_y); + + cr = cairo_create (surface); + cairo_translate (cr, 0, bounds->size.height); + cairo_scale (cr, 1, -1); + cairo_translate (cr, - bounds->origin.x, - bounds->origin.y); + + switch (fill_rule) + { + case GSK_FILL_RULE_WINDING: + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING); + break; + case GSK_FILL_RULE_EVEN_ODD: + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + break; + default: + g_assert_not_reached (); + break; + } + + gsk_path_to_cairo (path, cr); + cairo_clip (cr); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_paint (cr); + + cairo_destroy (cr); + + bytes = g_bytes_new_take (buffer, stride * height); + mask = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride); + g_bytes_unref (bytes); + + cairo_surface_destroy (surface); + + return mask; +} + +static inline void +render_fill_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + GskRenderNode *child = gsk_fill_node_get_child (node); + TextureRegion child_region; + gboolean is_offscreen1; + GskPath *path; + GskFillRule fill_rule; + graphene_rect_t mask_bounds; + int mask_texture_id; + OpMask *op; + float scale_x = builder->scale_x; + float scale_y = builder->scale_y; + + if (!add_offscreen_ops (self, builder, + &node->bounds, + child, + &child_region, &is_offscreen1, + FORCE_OFFSCREEN | RESET_CLIP)) + { + gsk_gl_renderer_add_render_ops (self, child, builder); + return; + } + + path = gsk_fill_node_get_path (node); + fill_rule = gsk_fill_node_get_fill_rule (node); + mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache, + path, + fill_rule, + NULL, + scale_x, scale_y, + &mask_bounds); + if (mask_texture_id == 0) + { + GdkTexture *mask; + + mask = make_path_mask (path, fill_rule, scale_x, scale_y, &mask_bounds); + + mask_texture_id = + gsk_gl_driver_get_texture_for_texture (self->gl_driver, + mask, + GL_LINEAR, + GL_LINEAR); + + gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id); + gsk_gl_path_cache_commit (&self->path_cache, + path, + fill_rule, + NULL, + scale_x, scale_y, + mask_texture_id, + &mask_bounds); + + g_object_unref (mask); + } + + ops_set_program (builder, &self->programs->mask_program); + + op = ops_begin (builder, OP_CHANGE_MASK); + op->mask = mask_texture_id; + op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width; + op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height; + op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) / node->bounds.size.width; + op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) / node->bounds.size.height; + + ops_set_texture (builder, child_region.texture_id); + + load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); +} + +static inline void +render_stroke_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + GskRenderNode *child = gsk_stroke_node_get_child (node); + TextureRegion child_region; + gboolean is_offscreen1; + GskPath *path; + GskStroke *stroke; + int mask_texture_id; + graphene_rect_t mask_bounds; + OpMask *op; + float scale_x = builder->scale_x; + float scale_y = builder->scale_y; + + if (!add_offscreen_ops (self, builder, + &node->bounds, + child, + &child_region, &is_offscreen1, + FORCE_OFFSCREEN | RESET_CLIP)) + { + gsk_gl_renderer_add_render_ops (self, child, builder); + return; + } + + path = gsk_stroke_node_get_path (node); + stroke = (GskStroke *)gsk_stroke_node_get_stroke (node); + + mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache, + path, + GSK_FILL_RULE_EVEN_ODD, + stroke, + scale_x, scale_y, + &mask_bounds); + if (mask_texture_id == 0) + { + GskPath *stroke_path; + GdkTexture *mask; + + stroke_path = gsk_path_stroke (path, stroke); + mask = make_path_mask (stroke_path, GSK_FILL_RULE_EVEN_ODD, scale_x, scale_y, &mask_bounds); + + mask_texture_id = + gsk_gl_driver_get_texture_for_texture (self->gl_driver, + mask, + GL_LINEAR, + GL_LINEAR); + + gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id); + gsk_gl_path_cache_commit (&self->path_cache, + path, + GSK_FILL_RULE_EVEN_ODD, + stroke, + scale_x, scale_y, + mask_texture_id, + &mask_bounds); + g_object_unref (mask); + gsk_path_unref (stroke_path); + } + + ops_set_program (builder, &self->programs->mask_program); + + op = ops_begin (builder, OP_CHANGE_MASK); + op->mask = mask_texture_id; + op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width; + op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height; + op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) / node->bounds.size.width; + op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) / node->bounds.size.height; + + ops_set_texture (builder, child_region.texture_id); + + load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); +} + static inline void render_blend_node (GskGLRenderer *self, GskRenderNode *node, @@ -3493,7 +3704,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds); INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect); - /* We initialize the alpha uniform here, since the default value is important. * We can't do it in the shader like a reasonable person would because that doesn't * work in gles. */ @@ -3875,10 +4085,16 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, render_mask_node (self, node, builder); break; - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: case GSK_FILL_NODE: + render_fill_node (self, node, builder); + break; + case GSK_STROKE_NODE: + render_stroke_node (self, node, builder); + break; + + case GSK_REPEATING_LINEAR_GRADIENT_NODE: + case GSK_REPEATING_RADIAL_GRADIENT_NODE: case GSK_CAIRO_NODE: default: { diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h index 3d02866b83..148b6b8df8 100644 --- a/gsk/gl/gskglrenderopsprivate.h +++ b/gsk/gl/gskglrenderopsprivate.h @@ -181,6 +181,7 @@ struct _Program } glshader; struct { int mask_location; + int child_rect_location; int texture_rect_location; } mask; }; |