diff options
Diffstat (limited to 'src/cairo-gl-shaders.c')
-rw-r--r-- | src/cairo-gl-shaders.c | 223 |
1 files changed, 223 insertions, 0 deletions
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; +} |