diff options
-rw-r--r-- | src/cairo-gl-glyphs.c | 14 | ||||
-rw-r--r-- | src/cairo-gl-private.h | 12 | ||||
-rw-r--r-- | src/cairo-gl-shaders.c | 223 | ||||
-rw-r--r-- | src/cairo-gl-surface.c | 49 |
4 files changed, 272 insertions, 26 deletions
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index c0a52058..3f8d6d39 100644 --- a/src/cairo-gl-glyphs.c +++ b/src/cairo-gl-glyphs.c @@ -61,13 +61,16 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, cairo_status_t status; int width, height; GLenum internal_format, format, type; - cairo_bool_t has_alpha; + cairo_bool_t has_alpha, requires_shader; - if (! _cairo_gl_get_image_format_and_type (glyph_surface->pixman_format, + if (! _cairo_gl_get_image_format_and_type (glyph_surface->pixman_color_space, + glyph_surface->pixman_format, + &requires_shader, &internal_format, &format, &type, - &has_alpha)) + &has_alpha) || + requires_shader) { cairo_bool_t is_supported; @@ -76,12 +79,15 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, return clone->base.status; is_supported = - _cairo_gl_get_image_format_and_type (clone->pixman_format, + _cairo_gl_get_image_format_and_type (clone->pixman_color_space, + clone->pixman_format, + &requires_shader, &internal_format, &format, &type, &has_alpha); assert (is_supported); + assert (! requires_shader); glyph_surface = clone; } diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index d5f705d2..ae408709 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -44,6 +44,7 @@ #include "cairo-device-private.h" #include "cairo-rtree-private.h" +#include <pixman.h> #include <GL/glew.h> #include "cairo-gl.h" @@ -131,6 +132,7 @@ typedef struct _cairo_gl_context { cairo_gl_shader_program_t shaders[CAIRO_GL_SHADER_SOURCE_COUNT] [CAIRO_GL_SHADER_MASK_COUNT] [CAIRO_GL_SHADER_IN_COUNT]; + cairo_cache_t shader_cache; cairo_gl_surface_t *current_target; cairo_gl_surface_t *glyphs_temporary_mask; @@ -270,7 +272,9 @@ cairo_private void _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand); cairo_private cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, +_cairo_gl_get_image_format_and_type (pixman_color_space_t color_space, + pixman_format_code_t pixman_format, + cairo_bool_t *requires_shader, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha); @@ -352,6 +356,12 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx, cairo_gl_shader_in_t in, cairo_gl_shader_program_t **out_program); +cairo_private cairo_status_t +_cairo_gl_get_draw_shader (cairo_gl_context_t *ctx, + pixman_color_space_t color_space, + pixman_format_code_t format, + cairo_gl_shader_program_t **out_program); + slim_hidden_proto (cairo_gl_surface_create); slim_hidden_proto (cairo_gl_surface_create_for_texture); diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index 0a231598..48d85be0 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -467,6 +467,7 @@ create_shader_program (cairo_gl_shader_program_t *program, if (impl == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; + printf ("=== compiling ===\n\n%s\n\n%s\n\n", vertex_text, fragment_text); status = impl->compile_shader (&program->vertex_shader, GL_VERTEX_SHADER, vertex_text); @@ -912,3 +913,225 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx, *out_program = program; return CAIRO_STATUS_SUCCESS; } + +typedef struct _cairo_shader_cache_entry { + cairo_cache_entry_t base; + cairo_bool_t draw; /* TRUE for drawing, FALSE for reading */ + pixman_color_space_t color_space; + unsigned int format_index; + cairo_gl_shader_program_t program; +} cairo_shader_cache_entry_t; + +#define MAX_GL_SHADERS_PER_CONTEXT 8 + +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->draw == b->draw && + a->color_space == b->color_space && + a->format_index == b->format_index; +} + +static unsigned long +_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) +{ + return -(entry->draw) ^ ((entry->color_space << 8) | entry->format_index); +} + +static void +_cairo_gl_shader_cache_drestroy (void *data) +{ + cairo_shader_cache_entry_t *entry = data; + + destroy_shader_program (&entry->program); +} + +static cairo_status_t +_cairo_gl_init_shader_cache (cairo_gl_context_t *ctx) +{ + if (likely (ctx->shader_cache.hash_table)) + return CAIRO_STATUS_SUCCESS; + + if (! ctx->using_glsl) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_cache_init (&ctx->shader_cache, + _cairo_gl_shader_cache_equal, + NULL, + _cairo_gl_shader_cache_drestroy, + MAX_GL_SHADERS_PER_CONTEXT); +} + +const char *vs_draw_sources[] = { + "varying vec2 texcoords0;\n" + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + " texcoords0 = gl_MultiTexCoord0.xy;\n" + "}\n", + + "varying vec2 texcoords0;\n" + "varying vec2 texcoords1;\n" + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + " texcoords0 = gl_MultiTexCoord0.xy;\n" + " texcoords0 = gl_MultiTexCoord1.xy;\n" + "}\n", + + "varying vec2 texcoords0;\n" + "varying vec2 texcoords1;\n" + "varying vec2 texcoords2;\n" + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + " texcoords0 = gl_MultiTexCoord0.xy;\n" + " texcoords0 = gl_MultiTexCoord1.xy;\n" + " texcoords0 = gl_MultiTexCoord2.xy;\n" + "}\n" +}; + +const char *fs_draw_colorspace[] = { + [PIXMAN_COLOR_SPACE_ARGB] = + "vec4 transform_color(vec4 color)\n" + "{\n" + " return color;\n" + "}\n", + [PIXMAN_COLOR_SPACE_ARGB_UNMULTIPLIED] = + "vec4 transform_color(vec4 color)\n" + "{\n" + " return vec4 (color.rgb * color.a, color.a);\n" + "}\n", + [PIXMAN_COLOR_SPACE_YCBCR_HD] = + "mat3 yuv2rgb = mat3 (1.16438, 0, 1.79274,\n" + " 1.16438, -0.213249, -0.532909,\n" + " 1.16438, 2.1124, 0);\n" + "vec3 rgbadd = vec3 (-0.972945, 0.301483, -1.1334);\n" + "vec4 transform_color(vec4 color)\n" + "{\n" + " return vec4 ((color.rgb * yuv2rgb + rgbadd) * color.a, color.a);\n" + "}\n", + [PIXMAN_COLOR_SPACE_YCBCR_SD] = + "mat3 yuv2rgb = mat3 (1.16438, 0, 1.59603,\n" + " 1.16438, -0.391762, -0.812968,\n" + " 1.16438, 2.01723, 0);\n" + "vec3 rgbadd = vec3 (-0.874202, 0.531668, -1.08563);\n" + "vec4 transform_color(vec4 color)\n" + "{\n" + " return vec4 ((color.rgb * yuv2rgb + rgbadd) * color.a, color.a);\n" + "}\n", + [PIXMAN_COLOR_SPACE_YCBCR_JPEG] = + "vec4 transform_color(vec4 color)\n" + "{\n" + " return vec4(1.0, 0.0, 0.0, 1.0);\n" + "}\n", +}; + +const char *fs_draw_main[] = { + "uniform sampler2D texture0;\n" + "varying vec2 texcoords0;\n" + "void main()\n" + "{\n" + " gl_FragColor = transform_color (texture2D(texture0, texcoords0));\n" + "}\n" +}; + +static unsigned int +_cairo_gl_draw_shader_get_index_for_format (pixman_format_code_t format) +{ + switch ((unsigned int) format) { + default: + return 0; + } +} + +cairo_status_t +_cairo_gl_get_draw_shader (cairo_gl_context_t *ctx, + pixman_color_space_t color_space, + pixman_format_code_t format, + cairo_gl_shader_program_t **out_program) +{ + cairo_shader_cache_entry_t lookup, *entry; + cairo_status_t status; + const char *colorspace_source, *main_source; + char *fs_source; + unsigned int i, format_index; + + status = _cairo_gl_init_shader_cache (ctx); + if (unlikely (status)) + return status; + + format_index = _cairo_gl_draw_shader_get_index_for_format (format); + + lookup.draw = TRUE; + lookup.color_space = color_space; + lookup.format_index = format_index; + lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); + lookup.base.size = 1; + + entry = _cairo_cache_lookup (&ctx->shader_cache, &lookup.base); + if (entry) { + *out_program = &entry->program; + return CAIRO_STATUS_SUCCESS; + } + + /* no special shader necessary */ + if (format_index == 0 && + color_space == CAIRO_COLOR_SPACE_ARGB) { + return _cairo_gl_get_program (ctx, + CAIRO_GL_SHADER_SOURCE_TEXTURE, + CAIRO_GL_SHADER_MASK_NONE, + CAIRO_GL_SHADER_IN_NORMAL, + out_program); + } + + assert (color_space < ARRAY_LENGTH (fs_draw_colorspace)); + colorspace_source = fs_draw_colorspace[color_space]; + main_source = fs_draw_main[format_index]; + + fs_source = _cairo_malloc (strlen(colorspace_source) + + strlen(main_source) + + 1); + if (unlikely (fs_source == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + sprintf (fs_source, "%s%s", colorspace_source, fs_draw_main[format_index]); + + entry = malloc (sizeof (cairo_shader_cache_entry_t)); + if (unlikely (entry == NULL)) { + free (fs_source); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + entry->draw = TRUE; + entry->color_space = color_space; + entry->format_index = format_index; + entry->base.hash = _cairo_gl_shader_cache_hash (&lookup); + entry->base.size = 1; + init_shader_program (&entry->program); + + assert (ARRAY_LENGTH (vs_draw_sources) >= pixman_format_num_planes (format)); + status = create_shader_program (&entry->program, + vs_draw_sources[pixman_format_num_planes (format) - 1], + fs_source); + free (fs_source); + + _cairo_gl_use_program (&entry->program); + for (i = 0; i < pixman_format_num_planes (format); i++) { + char name[20]; + sprintf (name, "texture%u", i); + status = bind_texture_to_shader (entry->program.program, name, i); + assert (status == CAIRO_STATUS_SUCCESS); + } + _cairo_gl_use_program (NULL); + status = _cairo_cache_insert (&ctx->shader_cache, &entry->base); + if (unlikely (status)) { + _cairo_gl_shader_cache_drestroy (entry); + return status; + } + + *out_program = &entry->program; + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index e73dd514..26c7955c 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -197,11 +197,14 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) } cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, +_cairo_gl_get_image_format_and_type (pixman_color_space_t color_space, + pixman_format_code_t pixman_format, + cairo_bool_t *requires_shader, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha) { *has_alpha = TRUE; + *requires_shader = color_space != CAIRO_COLOR_SPACE_ARGB; switch (pixman_format) { case PIXMAN_a8r8g8b8: @@ -788,18 +791,20 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, int dst_x, int dst_y) { GLenum internal_format, format, type; - cairo_bool_t has_alpha; + cairo_bool_t has_alpha, requires_shader; cairo_image_surface_t *clone = NULL; cairo_gl_context_t *ctx; int cpp; cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (src->pixman_color_space != PIXMAN_COLOR_SPACE_ARGB || - ! _cairo_gl_get_image_format_and_type (src->pixman_format, + if (! _cairo_gl_get_image_format_and_type (src->pixman_color_space, + src->pixman_format, + &requires_shader, &internal_format, &format, &type, - &has_alpha)) + &has_alpha) || + (requires_shader && ! ((cairo_gl_context_t *) dst->base.device)->using_glsl)) { cairo_bool_t is_supported; @@ -808,12 +813,15 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, return clone->base.status; is_supported = - _cairo_gl_get_image_format_and_type (clone->pixman_format, + _cairo_gl_get_image_format_and_type (clone->pixman_color_space, + clone->pixman_format, + &requires_shader, &internal_format, &format, &type, &has_alpha); assert (is_supported); + assert (! requires_shader); src = clone; } @@ -825,7 +833,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, glPixelStorei (GL_UNPACK_ALIGNMENT, 1); glPixelStorei (GL_UNPACK_ROW_LENGTH, pixman_image_get_stride (src->pixman_image) / cpp); - if (dst->fb) { + if (dst->fb && ! requires_shader) { glBindTexture (ctx->tex_target, dst->tex); glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -865,20 +873,19 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, GLfloat vertices[8], texcoords[8]; if (ctx->using_glsl) { - cairo_gl_shader_program_t *program; - - status = _cairo_gl_get_program (ctx, - CAIRO_GL_SHADER_SOURCE_TEXTURE, - CAIRO_GL_SHADER_MASK_NONE, - CAIRO_GL_SHADER_IN_NORMAL, - &program); - if (_cairo_status_is_error (status)) { - _cairo_gl_context_release (ctx); - goto fail; - } - - _cairo_gl_use_program (program); - } else { + cairo_gl_shader_program_t *program; + status = _cairo_gl_get_draw_shader (ctx, + src->pixman_color_space, + src->pixman_format, + &program); + if (_cairo_status_is_error (status)) { + _cairo_gl_context_release (ctx); + goto fail; + } + + _cairo_gl_use_program (program); + } else { + assert (! requires_shader); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } |