summaryrefslogtreecommitdiff
path: root/src/cairo-gl-shaders.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-shaders.c')
-rw-r--r--src/cairo-gl-shaders.c223
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;
+}