diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index e3e2a9960..768e5c90c 100644 --- a/src/cairo-gl-glyphs.c +++ b/src/cairo-gl-glyphs.c @@ -1,6 +1,7 @@ /* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson + * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -42,13 +43,6 @@ #define GLYPH_CACHE_MIN_SIZE 4 #define GLYPH_CACHE_MAX_SIZE 128 -typedef enum _cairo_gl_glyphs_shader { - CAIRO_GL_GLYPHS_SHADER_UNSET, - CAIRO_GL_GLYPHS_SHADER_NORMAL, - CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA, - CAIRO_GL_GLYPHS_SHADER_CA_SOURCE, -} cairo_gl_glyphs_shader_t; - typedef struct _cairo_gl_glyph_private { cairo_rtree_node_t node; cairo_gl_glyph_cache_t *cache; @@ -142,6 +136,128 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_gl_glyphs_setup_shaders (cairo_gl_context_t *ctx) +{ + static const char *vs_source_constant = + "varying vec2 mask_texcoords;\n" + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + " mask_texcoords = gl_MultiTexCoord1.xy;\n" + "}\n"; + static const char *vs_source_texture = + "varying vec2 source_texcoords;\n" + "varying vec2 mask_texcoords;\n" + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + " source_texcoords = gl_MultiTexCoord0.xy;\n" + " mask_texcoords = gl_MultiTexCoord1.xy;\n" + "}\n"; + static const char *glyphs_source_constant = + "uniform vec4 constant_source;\n" + "vec4 get_source()\n" + "{\n" + " return constant_source;\n" + "}\n"; + static const char *glyphs_source_texture = + "uniform sampler2D source_sampler;\n" + "varying vec2 source_texcoords;\n" + "vec4 get_source()\n" + "{\n" + " return texture2D(source_sampler, source_texcoords);\n" + "}\n"; + static const char *glyphs_source_texture_alpha = + "uniform sampler2D source_sampler;\n" + "varying vec2 source_texcoords;\n" + "vec4 get_source()\n" + "{\n" + " return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n" + "}\n"; + static const char *glyphs_in_normal = + "uniform sampler2D mask_sampler;\n" + "varying vec2 mask_texcoords;\n" + "void main()\n" + "{\n" + " vec4 mask = texture2D(mask_sampler, mask_texcoords);\n" + " gl_FragColor = get_source() * mask.a;\n" + "}\n"; + static const char *glyphs_in_component_alpha_source = + "uniform sampler2D mask_sampler;\n" + "varying vec2 mask_texcoords;\n" + "void main()\n" + "{\n" + " vec4 mask = texture2D(mask_sampler, mask_texcoords);\n" + " gl_FragColor = get_source() * mask;\n" + "}\n"; + static const char *glyphs_in_component_alpha_alpha = + "uniform sampler2D mask_sampler;\n" + "varying vec2 mask_texcoords;\n" + "void main()\n" + "{\n" + " vec4 mask = texture2D(mask_sampler, mask_texcoords);\n" + " gl_FragColor = get_source().a * mask;\n" + "}\n"; + const char *glyphs_source_sources[CAIRO_GL_GLYPHS_SOURCE_COUNT] = { + glyphs_source_constant, + glyphs_source_texture, + glyphs_source_texture_alpha, + }; + const char *glyphs_in_sources[CAIRO_GL_GLYPHS_SHADER_COUNT] = { + glyphs_in_normal, + glyphs_in_component_alpha_source, + glyphs_in_component_alpha_alpha, + }; + enum cairo_gl_glyphs_shader_source source; + enum cairo_gl_glyphs_shader in; + + for (source = 0; source < CAIRO_GL_GLYPHS_SOURCE_COUNT; source++) { + for (in = 0; in < CAIRO_GL_GLYPHS_SHADER_COUNT; in++) { + const char *source_source = glyphs_source_sources[source]; + const char *in_source = glyphs_in_sources[in]; + char *fs_source = _cairo_malloc (strlen(source_source) + + strlen(in_source) + + 1); + const char *vs_source; + cairo_status_t status; + cairo_gl_shader_program_t *program = &ctx->glyphs_shaders[source][in]; + + if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT) + vs_source = vs_source_constant; + else + vs_source = vs_source_texture; + + if (!fs_source) + return CAIRO_STATUS_NO_MEMORY; + + sprintf(fs_source, "%s%s", source_source, in_source); + + init_shader_program (program); + status = create_shader_program (program, + vs_source, + fs_source); + free (fs_source); + + if (_cairo_status_is_error (status)) + return status; + + _cairo_gl_use_program (program); + bind_texture_to_shader (program->program, "mask_sampler", 1); + if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT) { + vs_source = vs_source_constant; + } else { + bind_texture_to_shader (program->program, "source_sampler", 0); + vs_source = vs_source_texture; + } + } + } + + _cairo_gl_use_program (NULL); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_gl_glyph_private_t * _cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, cairo_scaled_glyph_t *scaled_glyph) @@ -254,9 +370,9 @@ typedef struct _cairo_gl_glyphs_setup } cairo_gl_glyphs_setup_t; static void -_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx, - cairo_gl_glyphs_setup_t *setup, - cairo_gl_glyphs_shader_t shader) +_cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx, + cairo_gl_glyphs_setup_t *setup, + cairo_gl_glyphs_shader_t shader) { if (setup->shader == shader) return; @@ -296,6 +412,46 @@ _cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); } +static void +_cairo_gl_glyphs_set_shader_glsl (cairo_gl_context_t *ctx, + cairo_gl_glyphs_setup_t *setup, + cairo_gl_glyphs_shader_t shader) +{ + enum cairo_gl_glyphs_shader_source glyphs_source; + + if (setup->shader == shader) + return; + + if (setup->composite->src.type == OPERAND_CONSTANT) { + glyphs_source = CAIRO_GL_GLYPHS_SOURCE_CONSTANT; + } else { + if (setup->composite->src.operand.texture.surface->base.content != + CAIRO_CONTENT_ALPHA) + { + glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE; + } else { + glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA; + } + } + + setup->composite->shader = &ctx->glyphs_shaders[glyphs_source][shader]; + _cairo_gl_use_program (setup->composite->shader); + _cairo_gl_set_src_operand (ctx, setup->composite); + + setup->shader = shader; +} + +static void +_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx, + cairo_gl_glyphs_setup_t *setup, + cairo_gl_glyphs_shader_t shader) +{ + if (ctx->using_glsl_glyphs) + _cairo_gl_glyphs_set_shader_glsl (ctx, setup, shader); + else + _cairo_gl_glyphs_set_shader_fixed (ctx, setup, shader); +} + static void _cairo_gl_glyphs_draw (cairo_gl_context_t *ctx, cairo_gl_glyphs_setup_t *setup) @@ -440,6 +596,8 @@ _render_glyphs (cairo_gl_surface_t *dst, *has_component_alpha = FALSE; + memset (&composite_setup, 0, sizeof(composite_setup)); + status = _cairo_gl_operand_init (&composite_setup.src, source, dst, glyph_extents->x, glyph_extents->y, dst_x, dst_y, @@ -452,6 +610,13 @@ _render_glyphs (cairo_gl_surface_t *dst, if (unlikely (status)) return status; + if (ctx->using_glsl && !ctx->glsl_glyphs_inited) { + cairo_status_t status = _cairo_gl_glyphs_setup_shaders (ctx); + if (!_cairo_status_is_error (status)) + ctx->using_glsl_glyphs = TRUE; + ctx->glsl_glyphs_inited = TRUE; + } + _cairo_gl_set_destination (dst); _cairo_scaled_font_freeze_cache (scaled_font); @@ -480,7 +645,7 @@ _render_glyphs (cairo_gl_surface_t *dst, if (setup.vbo_size > 4096) setup.vbo_size = 4096; setup.op = op; - setup.shader = CAIRO_GL_GLYPHS_SHADER_UNSET; + setup.shader = CAIRO_GL_GLYPHS_SHADER_COUNT; /* unset */ glGenBuffersARB (1, &vbo); glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo); @@ -596,6 +761,7 @@ _render_glyphs (cairo_gl_surface_t *dst, glDisableClientState (GL_TEXTURE_COORD_ARRAY); glActiveTexture (GL_TEXTURE1); glDisable (GL_TEXTURE_2D); + _cairo_gl_use_program (NULL); if (vbo != 0) { glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0); @@ -878,3 +1044,4 @@ _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) sizeof (cairo_gl_glyph_private_t), NULL); } + diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 927595a65..14b6489b5 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -87,6 +87,20 @@ typedef struct cairo_gl_shader_program { cairo_bool_t build_failure; } cairo_gl_shader_program_t; +enum cairo_gl_glyphs_shader_source { + CAIRO_GL_GLYPHS_SOURCE_CONSTANT, + CAIRO_GL_GLYPHS_SOURCE_TEXTURE, + CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA, + CAIRO_GL_GLYPHS_SOURCE_COUNT, +}; + +typedef enum cairo_gl_glyphs_shader { + CAIRO_GL_GLYPHS_SHADER_NORMAL, + CAIRO_GL_GLYPHS_SHADER_CA_SOURCE, + CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA, + CAIRO_GL_GLYPHS_SHADER_COUNT, +} cairo_gl_glyphs_shader_t; + typedef struct _cairo_gl_context { cairo_device_t base; @@ -94,9 +108,13 @@ typedef struct _cairo_gl_context { GLint max_framebuffer_size; GLint max_texture_size; cairo_bool_t using_glsl; + cairo_bool_t glsl_glyphs_inited; + cairo_bool_t using_glsl_glyphs; cairo_bool_t using_shaders; cairo_gl_shader_program_t fill_rectangles_shader; + cairo_gl_shader_program_t glyphs_shaders[CAIRO_GL_GLYPHS_SOURCE_COUNT] + [CAIRO_GL_GLYPHS_SHADER_COUNT]; cairo_gl_surface_t *current_target; cairo_gl_surface_t *glyphs_temporary_mask; @@ -134,6 +152,7 @@ typedef struct cairo_gl_composite_operand { typedef struct _cairo_gl_composite_setup { cairo_gl_composite_operand_t src; cairo_gl_composite_operand_t mask; + cairo_gl_shader_program_t *shader; } cairo_gl_composite_setup_t; cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index 7e6026378..b449b032d 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -317,7 +317,10 @@ bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit) static void use_program_arb (cairo_gl_shader_program_t *program) { - glUseProgramObjectARB (program->program); + if (program) + glUseProgramObjectARB (program->program); + else + glUseProgramObjectARB (0); } /* OpenGL Core 2.0 API. */ @@ -488,7 +491,10 @@ bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_un static void use_program_core_2_0 (cairo_gl_shader_program_t *program) { - glUseProgram (program->program); + if (program) + glUseProgram (program->program); + else + glUseProgram (0); } static const shader_impl_t shader_impl_core_2_0 = { diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index 41a9bbc2e..28524f25b 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -117,7 +117,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) if (GLEW_VERSION_2_0 || (GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader && - GLEW_ARB_shader_objects)) { + GLEW_ARB_shader_objects)) + { ctx->using_glsl = TRUE; } @@ -1160,32 +1161,43 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx, switch (setup->src.type) { case OPERAND_CONSTANT: - _cairo_gl_set_tex_combine_constant_color (ctx, 0, - setup->src.operand.constant.color); + if (setup->shader) { + bind_vec4_to_shader(setup->shader->program, + "constant_source", + setup->src.operand.constant.color[0], + setup->src.operand.constant.color[1], + setup->src.operand.constant.color[2], + setup->src.operand.constant.color[3]); + } else { + _cairo_gl_set_tex_combine_constant_color (ctx, 0, + setup->src.operand.constant.color); + } break; case OPERAND_TEXTURE: _cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex, src_attributes); - /* Set up the constant color we use to set color to 0 if needed. */ - glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color); - /* Set up the combiner to just set color to the sampled texture. */ - glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); - glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + if (!setup->shader) { + /* Set up the constant color we use to set color to 0 if needed. */ + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color); + /* Set up the combiner to just set color to the sampled texture. */ + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - /* Force the src color to 0 if the surface should be alpha-only. - * We may have a teximage with color bits if the implementation doesn't - * support GL_ALPHA FBOs. - */ - if (setup->src.operand.texture.surface->base.content != - CAIRO_CONTENT_ALPHA) - glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0); - else - glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); - glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0); - glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - break; + /* Force the src color to 0 if the surface should be + * alpha-only. We may have a teximage with color bits if + * the implementation doesn't support GL_ALPHA FBOs. + */ + if (setup->src.operand.texture.surface->base.content != + CAIRO_CONTENT_ALPHA) + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0); + else + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + break; + } } }