diff options
-rw-r--r-- | src/cairo-gl-device.c | 2 | ||||
-rw-r--r-- | src/cairo-gl-private.h | 8 | ||||
-rw-r--r-- | src/cairo-gl-shaders.c | 111 |
3 files changed, 100 insertions, 21 deletions
diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c index ed9b91ea..7d873dc8 100644 --- a/src/cairo-gl-device.c +++ b/src/cairo-gl-device.c @@ -68,6 +68,8 @@ _gl_finish (void *device) destroy_shader (ctx, ctx->vertex_shaders[i]); } + _cairo_cache_fini (&ctx->shaders); + _cairo_gl_context_release (ctx); } } diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 347735c7..fa8b87b9 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -65,6 +65,10 @@ #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED #endif +/* maximal number of shaders we keep in the cache. + * Random number that is hopefully big enough to not cause many cache evictions. */ +#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 + typedef struct _cairo_gl_surface { cairo_surface_t base; @@ -132,9 +136,7 @@ typedef struct _cairo_gl_context { GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1]; cairo_gl_shader_program_t fill_rectangles_shader; - cairo_gl_shader_program_t shaders[CAIRO_GL_OPERAND_COUNT] - [CAIRO_GL_OPERAND_COUNT] - [CAIRO_GL_SHADER_IN_COUNT]; + cairo_cache_t shaders; cairo_gl_surface_t *current_target; cairo_gl_glyph_cache_t glyph_cache[2]; diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index 1a4c9a0c..f1f0becf 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -430,9 +430,50 @@ static const cairo_gl_shader_impl_t shader_impl_arb = { use_program_arb, }; +typedef struct _cairo_shader_cache_entry { + cairo_cache_entry_t base; + + cairo_gl_operand_type_t src; + cairo_gl_operand_type_t mask; + cairo_gl_operand_type_t dest; + cairo_gl_shader_in_t in; + + cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ + cairo_gl_shader_program_t program; +} cairo_shader_cache_entry_t; + +static cairo_bool_t +_cairo_gl_shader_cache_equal (const void *key_a, const void *key_b) +{ + const cairo_shader_cache_entry_t *a = key_a; + const cairo_shader_cache_entry_t *b = key_b; + + return a->src == b->src && + a->mask == b->mask && + a->dest == b->dest && + a->in == b->in; +} + +static unsigned long +_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) +{ + return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in); +} + +static void +_cairo_gl_shader_cache_destroy (void *data) +{ + cairo_shader_cache_entry_t *entry = data; + + destroy_shader_program (entry->ctx, &entry->program); + free (entry); +} + void _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) { + cairo_status_t status; + /* XXX multiple device support? */ if (GLEW_VERSION_2_0) { ctx->shader_impl = &shader_impl_core_2_0; @@ -445,6 +486,12 @@ _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) } memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); + + status = _cairo_cache_init (&ctx->shaders, + _cairo_gl_shader_cache_equal, + NULL, + _cairo_gl_shader_cache_destroy, + CAIRO_GL_MAX_SHADERS_PER_CONTEXT); } void @@ -848,6 +895,7 @@ _cairo_gl_use_program (cairo_gl_context_t *ctx, ctx->shader_impl->use_program (program); } +#if 0 /** * This function reduces the GLSL program combinations we compile when * there are non-functional differences. @@ -871,6 +919,7 @@ _cairo_gl_select_program (cairo_gl_context_t *ctx, return &ctx->shaders[source][mask][in]; } +#endif cairo_status_t _cairo_gl_get_program (cairo_gl_context_t *ctx, @@ -879,22 +928,30 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx, cairo_gl_shader_in_t in, cairo_gl_shader_program_t **out_program) { - cairo_gl_shader_program_t *program; + cairo_shader_cache_entry_t lookup, *entry; char *fs_source; cairo_status_t status; - program = _cairo_gl_select_program(ctx, source, mask, in); - if (program->program) { - *out_program = program; - return CAIRO_STATUS_SUCCESS; - } - - if (program->build_failure) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (ctx->shader_impl == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; + lookup.src = source; + lookup.mask = mask; + lookup.dest = CAIRO_GL_OPERAND_NONE; + lookup.in = in; + lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); + lookup.base.size = 1; + + entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); + if (entry) { + if (entry->program.build_failure) + return CAIRO_INT_STATUS_UNSUPPORTED; + + assert (entry->program.program); + *out_program = &entry->program; + return CAIRO_STATUS_SUCCESS; + } + fs_source = cairo_gl_shader_get_fragment_source (ctx->tex_target, in, source, @@ -903,31 +960,49 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx, if (unlikely (fs_source == NULL)) return CAIRO_STATUS_NO_MEMORY; - init_shader_program (program); + entry = malloc (sizeof (cairo_shader_cache_entry_t)); + if (unlikely (entry == NULL)) { + free (fs_source); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); + + entry->ctx = ctx; + init_shader_program (&entry->program); status = create_shader_program (ctx, - program, + &entry->program, cairo_gl_operand_get_var_type (source), cairo_gl_operand_get_var_type (mask), fs_source); free (fs_source); - if (_cairo_status_is_error (status)) + if (unlikely (status)) { + /* still add to cache, so we know we got a build failure */ + if (_cairo_status_is_error (status) || + _cairo_cache_insert (&ctx->shaders, &entry->base)) { + free (entry); + } + return status; + } - _cairo_gl_use_program (ctx, program); + _cairo_gl_use_program (ctx, &entry->program); if (source != CAIRO_GL_OPERAND_CONSTANT) { - status = bind_texture_to_shader (ctx, program->program, "source_sampler", 0); + status = bind_texture_to_shader (ctx, entry->program.program, "source_sampler", 0); assert (!_cairo_status_is_error (status)); } if (mask != CAIRO_GL_OPERAND_CONSTANT && mask != CAIRO_GL_OPERAND_SPANS && mask != CAIRO_GL_OPERAND_NONE) { - status = bind_texture_to_shader (ctx, program->program, "mask_sampler", 1); + status = bind_texture_to_shader (ctx, entry->program.program, "mask_sampler", 1); assert (!_cairo_status_is_error (status)); } + status = _cairo_cache_insert (&ctx->shaders, &entry->base); + _cairo_gl_use_program (ctx, NULL); - *out_program = program; - return CAIRO_STATUS_SUCCESS; + *out_program = &entry->program; + return status; } |