PATCH: GL: Create glyph mask surface only once per context, enlarge on demand
Mikko Strandborg <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <CD440761.C519%[email protected]> |
Hello, In text rendering, when the rendering happens via a mask, the GL surface allocates and releases a temporary mask surface for each text rendering operation. This is very slow. The attached patch stores the mask surface to GL context. It is created lazily on first rendering, and enlarged on demand. This makes text rendering some 250% faster in some cases. --Mikko -- cairo mailing list [email protected] http://lists.cairographics.org/mailman/listinfo/cairo
0001-GL-Create-glyph-mask-only-once-per-GL-context-enlarg.patch
(application/octet-stream, 5.8 KB)
From 50e7162186cd92d130879bb2bdccd79d97b43ac9 Mon Sep 17 00:00:00 2001 From: Mikko Strandborg <[email protected]> Date: Wed, 13 Feb 2013 13:23:13 +0200 Subject: [PATCH] GL: Create glyph mask only once per GL context, enlarge on demand --- src/cairo-gl-device.c | 8 ++++++ src/cairo-gl-glyphs.c | 73 ++++++++++++++++++++++++++++++++++++++++-------- src/cairo-gl-private.h | 3 ++ 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c index 22297b3..b7e679f 100644 --- a/src/cairo-gl-device.c +++ b/src/cairo-gl-device.c @@ -122,6 +122,12 @@ _gl_destroy (void *device) ctx->acquire (ctx); + if(ctx->glyph_mask) + { + cairo_surface_destroy(ctx->glyph_mask); + ctx->glyph_mask = NULL; + } + while (! cairo_list_is_empty (&ctx->fonts)) { cairo_gl_font_t *font; @@ -285,6 +291,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) ctx->current_operator = -1; ctx->gl_flavor = gl_flavor; + ctx->glyph_mask = NULL; + status = _cairo_gl_context_init_shaders (ctx); if (unlikely (status)) return status; diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index 34b5d72..1cd3e86 100644 --- a/src/cairo-gl-glyphs.c +++ b/src/cairo-gl-glyphs.c @@ -219,6 +219,25 @@ cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, return CAIRO_STATUS_SUCCESS; } +/* Clear a partial surface, assumes context has already been acquired */ +static void _cairo_gl_surface_clear_with_extent( cairo_gl_context_t *ctx, + cairo_gl_surface_t * dst, + cairo_rectangle_int_t *extent, + const cairo_color_t *color, + cairo_bool_t use_multisample ) +{ + _cairo_gl_context_set_destination(ctx, dst, use_multisample); + glClearColor(color->red, color->green, color->blue, color->alpha); + ctx->states_cache.clear_red = color->red; + ctx->states_cache.clear_green = color->green; + ctx->states_cache.clear_blue = color->blue; + ctx->states_cache.clear_alpha = color->alpha; + glEnable(GL_SCISSOR_TEST); + glScissor(extent->x, extent->y, extent->width, extent->height); + glClear(GL_COLOR_BUFFER_BIT); +} + + static cairo_status_t render_glyphs (cairo_gl_surface_t *dst, int dst_x, int dst_y, @@ -357,18 +376,50 @@ render_glyphs_via_mask (cairo_gl_surface_t *dst, cairo_surface_t *mask; cairo_status_t status; cairo_bool_t has_component_alpha; + cairo_gl_context_t *ctx; TRACE ((stderr, "%s\n", __FUNCTION__)); - /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ - mask = cairo_gl_surface_create (dst->base.device, - CAIRO_CONTENT_COLOR_ALPHA, - info->extents.width, - info->extents.height); - if (unlikely (mask->status)) - return mask->status; + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + /* Lazily create the glyph mask */ + /* Delete previous glyph mask if it is too small */ + if(ctx->glyph_mask && + ( + ((cairo_gl_surface_t *)ctx->glyph_mask)->width < info->extents.width || + ((cairo_gl_surface_t *)ctx->glyph_mask)->height < info->extents.height ) + ) + { + cairo_surface_destroy(ctx->glyph_mask); + ctx->glyph_mask = NULL; + } + + /* Create the mask if it has not yet been initialized or it was too small and deleted above. */ + if(!ctx->glyph_mask) + { + /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ + ctx->glyph_mask = cairo_gl_surface_create (dst->base.device, + CAIRO_CONTENT_COLOR_ALPHA, + info->extents.width, + info->extents.height); + if (unlikely (ctx->glyph_mask->status)) + { + status = ctx->glyph_mask->status; + status = _cairo_gl_context_release(ctx, status); + return status; + } - status = render_glyphs ((cairo_gl_surface_t *) mask, + } + else + { + /* Reusing old glyph mask, clear it */ + _cairo_gl_surface_clear_with_extent(ctx, (cairo_gl_surface_t *)ctx->glyph_mask, + &info->extents, CAIRO_COLOR_TRANSPARENT, FALSE); + } + + status = render_glyphs ((cairo_gl_surface_t *) ctx->glyph_mask, info->extents.x, info->extents.y, CAIRO_OPERATOR_ADD, NULL, info, &has_component_alpha, NULL); @@ -377,8 +428,8 @@ render_glyphs_via_mask (cairo_gl_surface_t *dst, cairo_surface_pattern_t source_pattern; cairo_rectangle_int_t clip_extents; - mask->is_clear = FALSE; - _cairo_pattern_init_for_surface (&mask_pattern, mask); + ctx->glyph_mask->is_clear = FALSE; + _cairo_pattern_init_for_surface (&mask_pattern, ctx->glyph_mask); mask_pattern.base.has_component_alpha = has_component_alpha; mask_pattern.base.filter = CAIRO_FILTER_NEAREST; mask_pattern.base.extend = CAIRO_EXTEND_NONE; @@ -408,7 +459,7 @@ render_glyphs_via_mask (cairo_gl_surface_t *dst, _cairo_pattern_fini (&source_pattern.base); } - cairo_surface_destroy (mask); + status = _cairo_gl_context_release(ctx, status); return status; } diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 5c9193d..077f291 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -373,6 +373,9 @@ struct _cairo_gl_context { cairo_bool_t thread_aware; + /* Intermediate mask surface for glyph rendering. Created on first access, enlarged on demand. */ + cairo_surface_t *glyph_mask; + void (*acquire) (void *ctx); void (*release) (void *ctx); -- 1.7.9.5