summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cairo-gl-glyphs.c14
-rw-r--r--src/cairo-gl-private.h12
-rw-r--r--src/cairo-gl-shaders.c223
-rw-r--r--src/cairo-gl-surface.c49
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);
}