diff options
author | Alexandros Frantzis <alexandros.frantzis@linaro.org> | 2012-05-03 13:41:29 +0300 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2012-05-12 11:09:26 +0100 |
commit | 6867383017fcea0b1d5a4671b32382037ba9be3f (patch) | |
tree | 904842cc2b7d994cfd480719e553b88eab3aa81c | |
parent | 82f69d1ef798c2ebff13ec2b548c4877bf5e0e5b (diff) |
gl: Provide a shader implementation of repeat wrap modes
In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are
only available for NPOT textures if the GL_OES_texture_npot is supported.
This commit adds a shader implementation of these wrap modes for use by
devices that do not support GL_OES_texture_npot.
-rw-r--r-- | src/cairo-gl-composite.c | 10 | ||||
-rw-r--r-- | src/cairo-gl-device.c | 16 | ||||
-rw-r--r-- | src/cairo-gl-private.h | 1 | ||||
-rw-r--r-- | src/cairo-gl-shaders.c | 84 |
4 files changed, 90 insertions, 21 deletions
diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 760c3a4da..a7891b96d 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -168,10 +168,16 @@ _cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, wrap_mode = GL_CLAMP_TO_EDGE; break; case CAIRO_EXTEND_REPEAT: - wrap_mode = GL_REPEAT; + if (ctx->has_npot_repeat) + wrap_mode = GL_REPEAT; + else + wrap_mode = GL_CLAMP_TO_EDGE; break; case CAIRO_EXTEND_REFLECT: - wrap_mode = GL_MIRRORED_REPEAT; + if (ctx->has_npot_repeat) + wrap_mode = GL_MIRRORED_REPEAT; + else + wrap_mode = GL_CLAMP_TO_EDGE; break; default: wrap_mode = 0; diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c index 47ee94e29..3ecd51e3a 100644 --- a/src/cairo-gl-device.c +++ b/src/cairo-gl-device.c @@ -187,18 +187,20 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) /* Check for required extensions */ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { - if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two")) + if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two")) { ctx->tex_target = GL_TEXTURE_2D; - else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle")) + ctx->has_npot_repeat = TRUE; + } else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle")) { ctx->tex_target = GL_TEXTURE_RECTANGLE; - else + ctx->has_npot_repeat = FALSE; + } else return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - } - else { + } else { + ctx->tex_target = GL_TEXTURE_2D; if (_cairo_gl_has_extension ("GL_OES_texture_npot")) - ctx->tex_target = GL_TEXTURE_2D; + ctx->has_npot_repeat = TRUE; else - return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + ctx->has_npot_repeat = FALSE; } if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP && diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index a8e89ccd8..103eade56 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -319,6 +319,7 @@ struct _cairo_gl_context { cairo_gl_flavor_t gl_flavor; cairo_bool_t has_map_buffer; cairo_bool_t has_packed_depth_stencil; + cairo_bool_t has_npot_repeat; void (*acquire) (void *ctx); void (*release) (void *ctx); diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index b3af6f784..ea57efae5 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -319,8 +319,10 @@ typedef struct _cairo_shader_cache_entry { cairo_gl_shader_in_t in; GLint src_gl_filter; cairo_bool_t src_border_fade; + cairo_extend_t src_extend; GLint mask_gl_filter; cairo_bool_t mask_border_fade; + cairo_extend_t mask_extend; cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ cairo_gl_shader_t shader; @@ -331,12 +333,16 @@ _cairo_gl_shader_cache_equal_desktop (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; + cairo_bool_t both_have_npot_repeat = + a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; return a->src == b->src && a->mask == b->mask && a->dest == b->dest && a->use_coverage == b->use_coverage && - a->in == b->in; + a->in == b->in && + (both_have_npot_repeat || a->src_extend == b->src_extend) && + (both_have_npot_repeat || a->mask_extend == b->mask_extend); } /* @@ -349,6 +355,8 @@ _cairo_gl_shader_cache_equal_gles2 (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; + cairo_bool_t both_have_npot_repeat = + a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; return a->src == b->src && a->mask == b->mask && @@ -357,8 +365,10 @@ _cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b) a->in == b->in && a->src_gl_filter == b->src_gl_filter && a->src_border_fade == b->src_border_fade && + (both_have_npot_repeat || a->src_extend == b->src_extend) && a->mask_gl_filter == b->mask_gl_filter && - a->mask_border_fade == b->mask_border_fade; + a->mask_border_fade == b->mask_border_fade && + (both_have_npot_repeat || a->mask_extend == b->mask_extend); } static unsigned long @@ -642,9 +652,9 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, else { _cairo_output_stream_printf (stream, - " return texture2D%s (%s_sampler, %s_texcoords);\n" + " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n" "}\n", - rectstr, namestr, namestr); + rectstr, namestr, namestr, namestr); } break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: @@ -669,9 +679,9 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, else { _cairo_output_stream_printf (stream, - " return texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n" + " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n" "}\n", - rectstr, namestr, namestr); + rectstr, namestr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: @@ -706,9 +716,10 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, else { _cairo_output_stream_printf (stream, - " return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2(t, 0.5)), is_valid);\n" + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n" + " return mix (vec4 (0.0), texel, is_valid);\n" "}\n", - rectstr, namestr); + rectstr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: @@ -750,9 +761,10 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, else { _cairo_output_stream_printf (stream, - " return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2 (upper_t, 0.5)), has_color);\n" + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" + " return mix (vec4 (0.0), texel, has_color);\n" "}\n", - rectstr, namestr); + rectstr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: @@ -778,11 +790,12 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" " \n" " float upper_t = mix (t.y, t.x, is_valid.x);\n" - " return mix (vec4 (0.0), texture2D%s (%s_sampler, vec2 (upper_t, 0.5)), has_color);\n" + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" + " return mix (vec4 (0.0), texel, has_color);\n" "}\n", namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, rectstr, namestr); + namestr, namestr, namestr, rectstr, namestr, namestr); break; } } @@ -838,6 +851,47 @@ _cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream, _cairo_output_stream_printf (stream, "}\n"); } +/* + * Emits the wrap function used by an operand. + * + * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are + * only available for NPOT textures if the GL_OES_texture_npot is supported. + * If GL_OES_texture_npot is not supported, we need to implement the wrapping + * functionality in the shader. + */ +static void +_cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, + cairo_output_stream_t *stream, + cairo_gl_operand_t *operand, + cairo_gl_tex_t name) +{ + const char *namestr = operand_names[name]; + cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); + + _cairo_output_stream_printf (stream, + "vec2 %s_wrap(vec2 coords)\n" + "{\n", + namestr); + + if (! ctx->has_npot_repeat && + (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) + { + if (extend == CAIRO_EXTEND_REPEAT) { + _cairo_output_stream_printf (stream, + " return fract(coords);\n"); + } else { /* CAIRO_EXTEND_REFLECT */ + _cairo_output_stream_printf (stream, + " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); + } + } + else + { + _cairo_output_stream_printf (stream, " return coords;\n"); + } + + _cairo_output_stream_printf (stream, "}\n"); +} + static cairo_status_t cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, cairo_gl_shader_in_t in, @@ -858,6 +912,9 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, "precision mediump float;\n" "#endif\n"); + _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE); + _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { if (_cairo_gl_shader_needs_border_fade (src)) _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE); @@ -1065,6 +1122,7 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, char *fs_source; cairo_status_t status; + lookup.ctx = ctx; lookup.src = source->type; lookup.mask = mask->type; lookup.dest = CAIRO_GL_OPERAND_NONE; @@ -1072,8 +1130,10 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, lookup.in = in; lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source); lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source); + lookup.src_extend = _cairo_gl_operand_get_extend (source); lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask); lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask); + lookup.mask_extend = _cairo_gl_operand_get_extend (mask); lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); lookup.base.size = 1; |