[cairo] [patch] gl: create BGRA texture and avoid conversion

Henry (Yu) Song - SISA hsong at sisa.samsung.com
Wed Jan 30 11:48:00 PST 2013


>From 73287550e6eeb68d6c9f08767d1dc4ad6d4f6905 Mon Sep 17 00:00:00 2001
From: Henry Song <henry.song at samsung.com>
Date: Wed, 30 Jan 2013 10:43:56 -0800
Subject: [PATCH] gl: create BGRA texture if driver supports, and consolidate
conversion from BGRA to RGBA into one function

If GLES2 driver supports GL_EXT_read_bgra, we can create a texture with
BGRA format.  The benefit of this is cairo does not need to do conversion
when upload BGRA image which is the default format used by cairo.  This
does not affect GL drivers, where we always create RGBA textures.

There are two map_to_image() function.  One is public cairo_surface_map_to_image()
where it does conversion from CAIRO_FORMAT_INVALID TO CAIRO_FORMAT_ARGB32, the
second one is private _cairo_surface_map_to_image () where it does not do
conversion. This can be confusion for any compositor implementation.  This patch
centralizes conversion of BGRA to RGBA to one single function. So regardless
which functions to call, the conversion will always be done when unmap image


---
 src/cairo-gl-device.c           |   28 ++++-----
 src/cairo-gl-private.h          |    4 +-
 src/cairo-gl-surface.c          |  126 +++++++++++++++++++++++++++++++++------
 src/cairo-gl-traps-compositor.c |   30 ----------
 4 files changed, 120 insertions(+), 68 deletions(-)

diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
index 22297b3..2086c8f 100644
--- a/src/cairo-gl-device.c
+++ b/src/cairo-gl-device.c
@@ -164,22 +164,6 @@ _cairo_gl_msaa_compositor_enabled (void)
     return env && strcmp(env, "msaa") == 0;
 }
 
-static cairo_bool_t
-test_can_read_bgra (cairo_gl_flavor_t gl_flavor)
-{
-    /* Desktop GL always supports BGRA formats. */
-    if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
-	return TRUE;
-
-    assert (gl_flavor == CAIRO_GL_FLAVOR_ES);
-
-   /* For OpenGL ES we have to look for the specific extension and BGRA only
-    * matches cairo's integer packed bytes on little-endian machines. */
-    if (!_cairo_is_little_endian())
-	return FALSE;
-    return _cairo_gl_has_extension ("EXT_read_format_bgra");
-}
-
 cairo_status_t
 _cairo_gl_context_init (cairo_gl_context_t *ctx)
 {
@@ -242,8 +226,6 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     ctx->has_map_buffer =
 	is_desktop || (is_gles && _cairo_gl_has_extension ("GL_OES_mapbuffer"));
 
-    ctx->can_read_bgra = test_can_read_bgra (gl_flavor);
-
     ctx->has_mesa_pack_invert =
 	_cairo_gl_has_extension ("GL_MESA_pack_invert");
 
@@ -316,6 +298,16 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     ctx->max_textures = 0;
     glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures);
 
+    ctx->supports_bgra = FALSE;
+#if CAIRO_HAS_GLESV2_SURFACE
+    /* if the driver supports both extensions, we are ensured that we can
+     * create texture, upload image and read texture with BGRA format
+     */
+    if (_cairo_gl_has_extension ("GL_EXT_read_format_bgra")) {
+	ctx->supports_bgra = TRUE;
+    }
+#endif
+
     for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
 	_cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index a75afa7..1c07ea0 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -183,6 +183,8 @@ struct _cairo_gl_surface {
     cairo_bool_t needs_update;
 
     cairo_region_t *clip_region;
+
+    GLenum texture_format;
 };
 
 typedef struct cairo_gl_glyph_cache {
@@ -366,9 +368,9 @@ struct _cairo_gl_context {
     cairo_bool_t has_map_buffer;
     cairo_bool_t has_packed_depth_stencil;
     cairo_bool_t has_npot_repeat;
-    cairo_bool_t can_read_bgra;
 
     cairo_bool_t thread_aware;
+    cairo_bool_t supports_bgra;
 
     void (*acquire) (void *ctx);
     void (*release) (void *ctx);
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 922f234..687ccf0 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -48,6 +48,7 @@
 #include "cairo-error-private.h"
 #include "cairo-image-surface-inline.h"
 #include "cairo-surface-backend-private.h"
+#include "cairo-surface-offset-private.h"
 
 static const cairo_surface_backend_t _cairo_gl_surface_backend;
 
@@ -391,6 +392,7 @@ _cairo_gl_surface_init (cairo_device_t *device,
     surface->width = width;
     surface->height = height;
     surface->needs_update = FALSE;
+    surface->texture_format = GL_BGRA;
 
     _cairo_gl_surface_embedded_operand_init (surface);
 }
@@ -454,7 +456,13 @@ _create_scratch_internal (cairo_gl_context_t *ctx,
     default:
 	ASSERT_NOT_REACHED;
     case CAIRO_CONTENT_COLOR_ALPHA:
-	format = GL_RGBA;
+	/* Create BGRA format texture if the driver supports it. This
+	 * avoid extra format conversion between RGBA <-> BGRA
+	 */
+	if (ctx->supports_bgra)
+	    format = GL_BGRA;
+	else
+	    format = GL_RGBA;
 	break;
     case CAIRO_CONTENT_ALPHA:
 	/* When using GL_ALPHA, compositing doesn't work properly, but for
@@ -462,8 +470,12 @@ _create_scratch_internal (cairo_gl_context_t *ctx,
 	 * an issue. */
 	if (for_caching)
 	    format = GL_ALPHA;
-	else
-	    format = GL_RGBA;
+	else {
+	    if (ctx->supports_bgra)
+		format = GL_BGRA;
+	    else
+		format = GL_RGBA;
+	}
 	break;
     case CAIRO_CONTENT_COLOR:
 	/* GL_RGB is almost what we want here -- sampling 1 alpha when
@@ -474,10 +486,14 @@ _create_scratch_internal (cairo_gl_context_t *ctx,
 	 * specified.  So, we have to store RGBA, and fill the alpha
 	 * channel with 1 when blending.
 	 */
-	format = GL_RGBA;
+	if (ctx->supports_bgra)
+	    format = GL_BGRA;
+	else
+	    format = GL_RGBA;
 	break;
     }
 
+    surface->texture_format = format;
     glTexImage2D (ctx->tex_target, 0, format, width, height, 0,
 		  format, GL_UNSIGNED_BYTE, NULL);
 
@@ -835,6 +851,60 @@ _cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst,
     return status;
 }
 
+static cairo_image_surface_t *
+_cairo_gl_convert_image (cairo_gl_context_t *ctx,
+			 cairo_gl_surface_t *dst,
+			 cairo_image_surface_t *src,
+			 int src_x, int src_y,
+			 int width, int height)
+{
+    cairo_int_status_t status;
+    cairo_surface_pattern_t pattern;
+    cairo_surface_t *rgba_src;
+    pixman_format_code_t pixman_format;
+
+    /* There is no need to create a RGBA format image in the following
+     * 3 conditions:
+     *
+     * 1. driver is not GLES 2 driver - driver does the conversion
+     * 2. if GLES 2 driver supports BGRA texture and can download BGRA image
+     * 3. a gl surface is created with GL_ALPHA texture - this is
+     *    only used for glyph texture cache
+     */
+    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP     ||
+	ctx->supports_bgra                            ||
+	(src->base.content == CAIRO_CONTENT_ALPHA &&
+	 dst->texture_format == GL_ALPHA &&
+	 dst->tex && 
+	 dst->owns_tex))
+	return (cairo_image_surface_t *)cairo_surface_reference (&src->base);
+
+    /* We must convert the src to RGBA format because when a texture is
+     * created, it is created with RGBA format */
+    pixman_format = _cairo_is_little_endian() ? 
+	PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
+
+    if (pixman_format == src->pixman_format)
+	return (cairo_image_surface_t *)cairo_surface_reference (&src->base);
+	
+    rgba_src = _cairo_image_surface_create_with_pixman_format (NULL,
+							       pixman_format,
+							       width,
+							       height,
+							       0);
+    if (unlikely (rgba_src->status))
+	return (cairo_image_surface_t *)rgba_src;
+
+    _cairo_pattern_init_for_surface (&pattern, &src->base);
+    status = _cairo_surface_offset_paint (rgba_src,
+					  src_x, src_y,
+					  CAIRO_OPERATOR_SOURCE,
+					  &pattern.base, NULL);
+    _cairo_pattern_fini (&pattern.base);
+
+    return (cairo_image_surface_t *)rgba_src;
+}
+
 cairo_status_t
 _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 			      cairo_image_surface_t *src,
@@ -845,6 +915,8 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
     GLenum internal_format, format, type;
     cairo_bool_t has_alpha, needs_swap;
     cairo_image_surface_t *clone = NULL;
+    cairo_image_surface_t *converted_src;
+
     cairo_gl_context_t *ctx;
     int cpp;
     cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
@@ -879,15 +951,30 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 	assert (!needs_swap);
 	src = clone;
     }
+    
+    /* let make sure the image src is converted */
+    converted_src = _cairo_gl_convert_image (ctx, dst, src,
+					     src_x, src_y,
+					     width, height);
+    if (unlikely (converted_src->base.status)) {
+	status = converted_src->base.status;
+	goto FAIL;
+    }
+
+    /* change src_x and src_y to 0 if we have created new image surface */
+    if (converted_src != src) {
+	src_x = 0;
+	src_y = 0;
+    }
 
-    cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+    cpp = PIXMAN_FORMAT_BPP (converted_src->pixman_format) / 8;
 
     status = _cairo_gl_surface_flush (&dst->base, 0);
     if (unlikely (status))
 	goto FAIL;
 
     if (_cairo_gl_surface_is_texture (dst)) {
-	void *data_start = src->data + src_y * src->stride + src_x * cpp;
+	void *data_start = converted_src->data + src_y * converted_src->stride + src_x * cpp;
 	void *data_start_gles2 = NULL;
 
 	/*
@@ -900,11 +987,12 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 	 */
 	if (src->stride < 0 ||
 	    (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
-	     (src->width * cpp < src->stride - 3 ||
-	      width != src->width)))
+	     (converted_src->width * cpp < src->stride - 3 ||
+	      width != converted_src->width)))
 	{
 	    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-	    status = _cairo_gl_surface_extract_image_data (src, src_x, src_y,
+	    status = _cairo_gl_surface_extract_image_data (converted_src, 
+							   src_x, src_y,
 							   width, height,
 							   &data_start_gles2);
 	    if (unlikely (status))
@@ -916,7 +1004,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 	{
 	    glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
 	    if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
-		glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+		glPixelStorei (GL_UNPACK_ROW_LENGTH, converted_src->stride / cpp);
 	}
 
         _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
@@ -948,7 +1036,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
             goto FAIL;
 
         status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
-                                               src,
+                                               converted_src,
                                                src_x, src_y,
                                                width, height,
                                                0, 0);
@@ -982,9 +1070,12 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 FAIL:
     status = _cairo_gl_context_release (ctx, status);
 
+    cairo_surface_destroy (&converted_src->base);
+
     if (clone)
         cairo_surface_destroy (&clone->base);
 
+
     return status;
 }
 
@@ -1079,17 +1170,14 @@ _cairo_gl_surface_map_to_image (void      *abstract_surface,
 	 * interacting with other image surfaces. For ALPHA, GLES2 does not
 	 * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the
 	 * pixman image that is created has row_stride = row_width * bpp. */
-	if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) {
+	if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->supports_bgra) {
 	    cairo_bool_t little_endian = _cairo_is_little_endian ();
 	    format = GL_RGBA;
 
-	    if (surface->base.content == CAIRO_CONTENT_COLOR) {
-		pixman_format = little_endian ?
-		    PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8;
-	    } else {
-		pixman_format = little_endian ?
-		    PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
-	    }
+	    /* FIXME: for CAIRO_CONTENT_COLOR, using 4-byte aligned 
+	     * GL_RGBA is more efficient */
+	    pixman_format = little_endian ?
+		PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
 	}
 
 	/* GLES2 only supports GL_UNSIGNED_BYTE. */
diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c
index b6c2333..620b3fd 100644
--- a/src/cairo-gl-traps-compositor.c
+++ b/src/cairo-gl-traps-compositor.c
@@ -302,36 +302,6 @@ traps_to_operand (void *_dst,
 	return image->status;
     }
 
-    /* GLES2 only supports RGB/RGBA when uploading */
-    if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) {
-	cairo_surface_pattern_t pattern;
-	cairo_surface_t *rgba_image;
-
-	/* XXX perform this fixup inside _cairo_gl_draw_image() */
-
-	rgba_image =
-	    _cairo_image_surface_create_with_pixman_format (NULL,
-							    _cairo_is_little_endian () ?  PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8,
-							    extents->width,
-							    extents->height,
-							    0);
-	if (unlikely (rgba_image->status))
-	    return rgba_image->status;
-
-	_cairo_pattern_init_for_surface (&pattern, image);
-	status = _cairo_surface_paint (rgba_image, CAIRO_OPERATOR_SOURCE,
-				       &pattern.base, NULL);
-	_cairo_pattern_fini (&pattern.base);
-
-	cairo_surface_destroy (image);
-	image = rgba_image;
-
-	if (unlikely (status)) {
-	    cairo_surface_destroy (image);
-	    return status;
-	}
-    }
-
     mask = _cairo_surface_create_similar_scratch (_dst,
 						  CAIRO_CONTENT_COLOR_ALPHA,
 						  extents->width,
-- 
1.7.9.5


More information about the cairo mailing list