summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2010-04-22 22:05:39 +0200
committerBenjamin Otte <otte@redhat.com>2010-04-23 23:34:46 +0200
commite3fb581b4cbd64b07813a1ab785373a7d30fd233 (patch)
treea5e38785746269067a90910dd41cd0299aed7a80
parent9fbbee95e275734360e7e91a5c88bf77ab2987eb (diff)
gl: Allow drawing image formats with special shaders
Extends the code to query formats and to draw images onto surfaces, so that it can use shaders. Implement shaders for the different color spaces.
-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);
}