[cairo-commit] 2 commits - src/cairo-gl-device.c src/cairo-gl-private.h src/cairo-gl-surface.c

Martin Robinson mrobinson at kemper.freedesktop.org
Mon Dec 10 01:18:33 PST 2012


 src/cairo-gl-device.c  |  136 ++++++++++++++++++++++++++++++++++++-------------
 src/cairo-gl-private.h |   14 +++++
 src/cairo-gl-surface.c |   82 ++++++++++++++++++-----------
 3 files changed, 168 insertions(+), 64 deletions(-)

New commits:
commit 9d9aa04b60e24542b6b2a4c6bf87115db7736c2f
Author: Martin Robinson <mrobinson at igalia.com>
Date:   Mon Dec 3 16:08:23 2012 -0800

    gl: Add BGRA download support for GLES2
    
    Some OpenGLES2 drivers support downloading BGRA data. On little-endian
    systems BGRA and GL_UNSIGNED_BYTe is equivalent to the typical
    cairo_image_t format, so this can prevent CPU bit swizzling for
    operations that involve images.

diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
index b6a9c15..5fbce5d 100644
--- a/src/cairo-gl-device.c
+++ b/src/cairo-gl-device.c
@@ -169,6 +169,22 @@ _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)
 {
@@ -230,6 +246,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
 			   (gl_flavor == CAIRO_GL_FLAVOR_ES &&
 			    _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");
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 088a874..538de57 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -357,6 +357,7 @@ 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;
 
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 7c93322..9cecbb6 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1022,6 +1022,11 @@ _cairo_gl_surface_map_to_image (void      *abstract_surface,
     cairo_status_t status;
     int y;
 
+    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+    if (unlikely (status)) {
+	return _cairo_image_surface_create_in_error (status);
+    }
+
     /* Want to use a switch statement here but the compiler gets whiny. */
     if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
 	format = GL_BGRA;
@@ -1043,25 +1048,26 @@ _cairo_gl_surface_map_to_image (void      *abstract_surface,
 	return NULL;
     }
 
-    /*
-     * GLES2 supports only RGBA, UNSIGNED_BYTE so use that.
-     * We are also using this format for ALPHA as 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 (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) {
-	format = GL_RGBA;
-	if (! _cairo_is_little_endian ()) {
-	    if (surface->base.content == CAIRO_CONTENT_COLOR)
-		pixman_format = PIXMAN_r8g8b8x8;
-	    else
-		pixman_format = PIXMAN_r8g8b8a8;
-	} else {
-	    if (surface->base.content == CAIRO_CONTENT_COLOR)
-		pixman_format = PIXMAN_x8b8g8r8;
-	    else
-		pixman_format = PIXMAN_a8b8g8r8;
+	/* If only RGBA is supported, we must download data in a compatible
+	 * format. This means that pixman will convert the data on the CPU when
+	 * 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) {
+	    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;
+	    }
 	}
+
+	/* GLES2 only supports GL_UNSIGNED_BYTE. */
 	type = GL_UNSIGNED_BYTE;
 	cpp = 4;
     }
@@ -1072,16 +1078,9 @@ _cairo_gl_surface_map_to_image (void      *abstract_surface,
 							extents->width,
 							extents->height,
 							-1);
-    if (unlikely (image->base.status))
-	return image;
-
-    if (surface->base.serial == 0)
+    if (unlikely (image->base.status) || surface->base.serial == 0) {
+	status = _cairo_gl_context_release (ctx, status);
 	return image;
-
-    status = _cairo_gl_context_acquire (surface->base.device, &ctx);
-    if (unlikely (status)) {
-	cairo_surface_destroy (&image->base);
-	return _cairo_image_surface_create_in_error (status);
     }
 
     cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);
commit a7d684e6fe4bbeea2262276aaa57bb2a47c401df
Author: Martin Robinson <mrobinson at igalia.com>
Date:   Mon Mar 5 23:11:19 2012 -0800

    gl/msaa: Share the depth/stencil buffer among all surfaces
    
    Instead of allocating a depth/stencil buffer for all surfaces, share a
    common buffer that's the size of the largest surface. This reduces
    video memory usage when there are many GL surfaces.

diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
index 37c837d..b6a9c15 100644
--- a/src/cairo-gl-device.c
+++ b/src/cairo-gl-device.c
@@ -112,6 +112,11 @@ _gl_finish (void *device)
     for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
 	_cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
 
+    if (ctx->depth_stencil_info.id)
+	ctx->dispatch.DeleteRenderbuffers (1, &ctx->depth_stencil_info.id);
+    if (ctx->msaa_depth_stencil_info.id)
+	ctx->dispatch.DeleteRenderbuffers (1, &ctx->msaa_depth_stencil_info.id);
+
     _gl_unlock (device);
 }
 
@@ -453,36 +458,59 @@ _cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx,
 }
 #endif
 
+static void
+_cairo_gl_replace_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
+					     int width,
+					     int height)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    if (ctx->msaa_depth_stencil_info.id)
+	dispatch->DeleteRenderbuffers (1, &ctx->msaa_depth_stencil_info.id);
+
+    dispatch->GenRenderbuffers (1, &ctx->msaa_depth_stencil_info.id);
+    dispatch->BindRenderbuffer (GL_RENDERBUFFER, ctx->msaa_depth_stencil_info.id);
+    dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER, ctx->num_samples,
+					      _get_depth_stencil_format (ctx),
+					      width, height);
+    ctx->msaa_depth_stencil_info.width = width;
+    ctx->msaa_depth_stencil_info.height = height;
+    ctx->msaa_depth_stencil_info.surfaces_with_same_size = 0;
+}
+
 static cairo_bool_t
 _cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
 					    cairo_gl_surface_t *surface)
 {
     cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
-    if (surface->msaa_depth_stencil)
-	return TRUE;
-
     _cairo_gl_ensure_framebuffer (ctx, surface);
 #if CAIRO_HAS_GL_SURFACE
     if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
 	_cairo_gl_ensure_multisampling (ctx, surface);
 #endif
 
-    dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil);
-    dispatch->BindRenderbuffer (GL_RENDERBUFFER,
-			        surface->msaa_depth_stencil);
+    if (! ctx->msaa_depth_stencil_info.id ||
+        ctx->msaa_depth_stencil_info.width < surface->width ||
+        ctx->msaa_depth_stencil_info.height < surface->height) {
+	_cairo_gl_replace_msaa_depth_stencil_buffer (ctx,
+						     surface->width,
+						     surface->height);
+    }
 
-    dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER,
-					      ctx->num_samples,
-					      _get_depth_stencil_format (ctx),
-					      surface->width,
-					      surface->height);
+    assert (ctx->msaa_depth_stencil_info.id);
+    if (surface->msaa_depth_stencil == ctx->msaa_depth_stencil_info.id)
+	return TRUE;
+
+    if (ctx->msaa_depth_stencil_info.width == surface->width &&
+	ctx->msaa_depth_stencil_info.height == surface->height)
+    ctx->msaa_depth_stencil_info.surfaces_with_same_size++;
 
 #if CAIRO_HAS_GL_SURFACE
     if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
 	dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
 					   GL_DEPTH_STENCIL_ATTACHMENT,
 					   GL_RENDERBUFFER,
-					   surface->msaa_depth_stencil);
+					   ctx->msaa_depth_stencil_info.id);
     }
 #endif
 
@@ -491,50 +519,72 @@ _cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
 	dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
 					   GL_DEPTH_ATTACHMENT,
 					   GL_RENDERBUFFER,
-					   surface->msaa_depth_stencil);
+					   ctx->msaa_depth_stencil_info.id);
 	dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
 					   GL_STENCIL_ATTACHMENT,
 					   GL_RENDERBUFFER,
-					   surface->msaa_depth_stencil);
+					   ctx->msaa_depth_stencil_info.id);
     }
 #endif
 
-    if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-	dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
-	surface->msaa_depth_stencil = 0;
-	return FALSE;
-    }
+    surface->msaa_depth_stencil = ctx->msaa_depth_stencil_info.id;
 
+    if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+	return FALSE;
     return TRUE;
 }
 
+static void
+_cairo_gl_replace_depth_stencil_buffer (cairo_gl_context_t *ctx,
+					int width,
+					int height)
+{
+    cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+    if (ctx->depth_stencil_info.id)
+	dispatch->DeleteRenderbuffers (1, &ctx->depth_stencil_info.id);
+
+    dispatch->GenRenderbuffers (1, &ctx->depth_stencil_info.id);
+    dispatch->BindRenderbuffer (GL_RENDERBUFFER, ctx->depth_stencil_info.id);
+    dispatch->RenderbufferStorage (GL_RENDERBUFFER,
+				   _get_depth_stencil_format (ctx),
+				   width, height);
+    ctx->depth_stencil_info.width = width;
+    ctx->depth_stencil_info.height = height;
+    ctx->depth_stencil_info.surfaces_with_same_size = 0;
+}
+
 static cairo_bool_t
 _cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx,
 				       cairo_gl_surface_t *surface)
 {
     cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
 
-    if (surface->depth_stencil)
-	return TRUE;
-
     _cairo_gl_ensure_framebuffer (ctx, surface);
 
-    dispatch->GenRenderbuffers (1, &surface->depth_stencil);
-    dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil);
-    dispatch->RenderbufferStorage (GL_RENDERBUFFER,
-				   _get_depth_stencil_format (ctx),
-				   surface->width, surface->height);
+    if (! ctx->depth_stencil_info.id ||
+        ctx->depth_stencil_info.width < surface->width ||
+        ctx->depth_stencil_info.height < surface->height) {
+	_cairo_gl_replace_depth_stencil_buffer (ctx,
+						surface->width,
+						surface->height);
+    }
+
+    if (surface->depth_stencil == ctx->depth_stencil_info.id)
+	return TRUE;
+
+    if (ctx->depth_stencil_info.width == surface->width &&
+	ctx->depth_stencil_info.height == surface->height)
+    ctx->depth_stencil_info.surfaces_with_same_size++;
 
     dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
-				       GL_RENDERBUFFER, surface->depth_stencil);
+				       GL_RENDERBUFFER, ctx->depth_stencil_info.id);
     dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-				       GL_RENDERBUFFER, surface->depth_stencil);
-    if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-	dispatch->DeleteRenderbuffers (1, &surface->depth_stencil);
-	surface->depth_stencil = 0;
-	return FALSE;
-    }
+				       GL_RENDERBUFFER, ctx->depth_stencil_info.id);
+    surface->depth_stencil = ctx->depth_stencil_info.id;
 
+    if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+	return FALSE;
     return TRUE;
 }
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 7c97c49..088a874 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -301,6 +301,13 @@ typedef struct _cairo_gl_dispatch {
 					     GLint level, GLsizei samples);
 } cairo_gl_dispatch_t;
 
+typedef struct _cairo_gl_shared_depth_stencil_info {
+    GLuint id;
+    int width;
+    int height;
+    unsigned surfaces_with_same_size;
+} cairo_gl_shared_depth_stencil_info_t;
+
 struct _cairo_gl_context {
     cairo_device_t base;
 
@@ -353,6 +360,12 @@ struct _cairo_gl_context {
 
     cairo_bool_t thread_aware;
 
+    /* GL stencil and depth buffers are shared among all surfaces
+       to preserve memory. In the future this could be a pool of renderbuffers
+       with an eviction policy. */
+    cairo_gl_shared_depth_stencil_info_t depth_stencil_info;
+    cairo_gl_shared_depth_stencil_info_t msaa_depth_stencil_info;
+
     void (*acquire) (void *ctx);
     void (*release) (void *ctx);
 
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index c0ee79f..7c93322 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -946,6 +946,23 @@ static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface)
     return ctx->gl_flavor;
 }
 
+static void
+_cairo_gl_shrink_shared_renderbuffer (cairo_gl_context_t *ctx,
+				      cairo_gl_shared_depth_stencil_info_t *info,
+				      int surface_width,
+				      int surface_height)
+{
+    if (info->width != surface_width || info->height != surface_height)
+	return;
+
+    /* Force the creation of a new shared renderbuffer if we are the
+     * last surface that has the same size as our old shared buffer. */
+    ctx->dispatch.DeleteRenderbuffers (1, &info->id);
+    info->height = 0;
+    info->width = 0;
+    info->id = 0;
+}
+
 static cairo_status_t
 _cairo_gl_surface_finish (void *abstract_surface)
 {
@@ -968,18 +985,24 @@ _cairo_gl_surface_finish (void *abstract_surface)
 
     if (surface->fb)
         ctx->dispatch.DeleteFramebuffers (1, &surface->fb);
-    if (surface->depth_stencil)
-        ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil);
     if (surface->owns_tex)
 	glDeleteTextures (1, &surface->tex);
 
+    if (surface->depth_stencil) {
+	_cairo_gl_shrink_shared_renderbuffer (ctx, &ctx->depth_stencil_info,
+					      surface->width, surface->height);
+    }
+
 #if CAIRO_HAS_GL_SURFACE
-    if (surface->msaa_depth_stencil)
-	ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
     if (surface->msaa_fb)
 	ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb);
     if (surface->msaa_rb)
 	ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
+
+    if (surface->msaa_depth_stencil) {
+	_cairo_gl_shrink_shared_renderbuffer (ctx, &ctx->msaa_depth_stencil_info,
+					      surface->width, surface->height);
+    }
 #endif
 
     return _cairo_gl_context_release (ctx, status);


More information about the cairo-commit mailing list