[PATCH 2/2] gl: Provide a shader implementation of repeat wrap modes

[email protected]
Newsgroups gmane.comp.lib.cairo
Message-ID <10ba5bf18ac1d603bffce71fca51d6b261d6f538.1336038723.git.alexandros.frantzis@linaro.org>
From: Alexandros Frantzis <[email protected]>

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.
---
 src/cairo-gl-composite.c |   10 ++++--
 src/cairo-gl-device.c    |   16 ++++++---
 src/cairo-gl-private.h   |    1 +
 src/cairo-gl-shaders.c   |   84 +++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c
index 633d229..1ab7557 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 47ee94e..3d3cc7e 100644
--- a/src/cairo-gl-device.c
+++ b/src/cairo-gl-device.c
@@ -187,18 +187,24 @@ _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 {
+	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 3afdd70..9cbd3a9 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 b3af6f7..ea57efa 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;
 
-- 
1.7.9.5

--
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.