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.
This commit is contained in:
Alexandros Frantzis 2012-05-03 13:41:29 +03:00 committed by Chris Wilson
parent 82f69d1ef7
commit 6867383017
4 changed files with 90 additions and 21 deletions

View file

@ -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;

View file

@ -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 &&

View file

@ -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);

View file

@ -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;