[cairo] [PATCH 6/6] [gl] Use GLSL for fill_rectangles when available.

Eric Anholt eric at anholt.net
Wed Jan 13 16:08:09 PST 2010


Ultimately, we want all of our paths to use shaders when they are
exposed -- it brings us closer to GL 3.0 compatibility and it should
reduce the work that GL drivers have to do per operation to compute
the required hardware state.
---
 src/Makefile.sources   |    2 +-
 src/cairo-gl-private.h |    6 ++
 src/cairo-gl-shaders.c |  115 ++++++++++++++++++++++++++++++++++++++++++++
 src/cairo-gl-surface.c |  125 ++++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 242 insertions(+), 6 deletions(-)
 create mode 100644 src/cairo-gl-shaders.c

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 1e04ed2..71d0abd 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -279,7 +279,7 @@ cairo_beos_headers = cairo-beos.h
 
 cairo_gl_headers = cairo-gl.h
 cairo_gl_private = cairo-gl-private.h
-cairo_gl_sources = cairo-gl-surface.c cairo-gl-glyphs.c
+cairo_gl_sources = cairo-gl-surface.c cairo-gl-glyphs.c cairo-gl-shaders.c
 cairo_glx_sources += cairo-glx-context.c
 cairo_eagle_sources += cairo-eagle-context.c
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 79145cf..5a32dba 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -85,6 +85,8 @@ struct _cairo_gl_context {
 
     cairo_mutex_t mutex; /* needed? */
     GLuint dummy_tex;
+    GLint fill_rectangles_shader;
+    GLint fill_rectangles_color_uniform;
     GLint max_framebuffer_size;
     GLint max_texture_size;
 
@@ -202,6 +204,10 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 cairo_private void
 _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
 
+cairo_private cairo_status_t
+_cairo_gl_load_glsl (GLint *shader,
+		     const char *vs_source, const char *fs_source);
+
 static inline int
 _cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
 {
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
new file mode 100644
index 0000000..645be9a
--- /dev/null
+++ b/src/cairo-gl-shaders.c
@@ -0,0 +1,115 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Eric Anholt
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Eric Anholt.
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+
+static GLint
+_cairo_gl_compile_glsl(GLenum type, GLint *shader_out, const char *source)
+{
+    GLint ok;
+    GLint shader;
+
+    shader = glCreateShaderObjectARB (type);
+    glShaderSourceARB (shader, 1, (const GLchar **)&source, NULL);
+    glCompileShaderARB (shader);
+    glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &ok);
+    if (!ok) {
+	GLchar *info;
+	GLint size;
+
+	glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB,
+				   &size);
+	info = malloc (size);
+
+	glGetInfoLogARB (shader, size, NULL, info);
+	fprintf (stderr, "Failed to compile %s: %s\n",
+		 type == GL_FRAGMENT_SHADER ? "FS" : "VS",
+		 info);
+	fprintf (stderr, "Shader source:\n%s", source);
+	fprintf (stderr, "GLSL compile failure\n");
+
+	glDeleteObjectARB (shader);
+
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *shader_out = shader;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_load_glsl (GLint *shader_out,
+		     const char *vs_source, const char *fs_source)
+{
+    GLint ok;
+    GLint shader, vs, fs;
+    cairo_status_t status;
+
+    shader = glCreateProgramObjectARB ();
+
+    status = _cairo_gl_compile_glsl (GL_VERTEX_SHADER_ARB, &vs, vs_source);
+    if (_cairo_status_is_error (status))
+	goto fail;
+    status = _cairo_gl_compile_glsl (GL_FRAGMENT_SHADER_ARB, &fs, fs_source);
+    if (_cairo_status_is_error (status))
+	goto fail;
+
+    glAttachObjectARB (shader, vs);
+    glAttachObjectARB (shader, fs);
+    glLinkProgram (shader);
+    glGetObjectParameterivARB (shader, GL_OBJECT_LINK_STATUS_ARB, &ok);
+    if (!ok) {
+	GLchar *info;
+	GLint size;
+
+	glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB,
+				   &size);
+	info = malloc (size);
+
+	glGetInfoLogARB (shader, size, NULL, info);
+	fprintf (stderr, "Failed to link: %s\n", info);
+	free (info);
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    *shader_out = shader;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail:
+    glDeleteObjectARB (shader);
+    return status;
+}
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index af658cb..9170f06 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1549,11 +1549,11 @@ _cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
 }
 
 static cairo_int_status_t
-_cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
-				   cairo_operator_t	    op,
-				   const cairo_color_t     *color,
-				   cairo_rectangle_int_t   *rects,
-				   int			    num_rects)
+_cairo_gl_surface_fill_rectangles_fixed (void			 *abstract_surface,
+					 cairo_operator_t	  op,
+					 const cairo_color_t     *color,
+					 cairo_rectangle_int_t   *rects,
+					 int			  num_rects)
 {
 #define N_STACK_RECTS 4
     cairo_gl_surface_t *surface = abstract_surface;
@@ -1632,6 +1632,121 @@ _cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
 #undef N_STACK_RECTS
 }
 
+static cairo_int_status_t
+_cairo_gl_surface_fill_rectangles_glsl (void                  *abstract_surface,
+					cairo_operator_t       op,
+					const cairo_color_t   *color,
+					cairo_rectangle_int_t *rects,
+					int                    num_rects)
+{
+#define N_STACK_RECTS 4
+    cairo_gl_surface_t *surface = abstract_surface;
+    GLfloat vertices_stack[N_STACK_RECTS*4*2];
+    GLfloat gl_color[4];
+    cairo_gl_context_t *ctx;
+    int i;
+    GLfloat *vertices;
+    static const char *fill_vs_source =
+	"void main()\n"
+	"{\n"
+	"	gl_Position = ftransform();\n"
+	"}\n";
+    static const char *fill_fs_source =
+	"uniform vec4 color;\n"
+	"void main()\n"
+	"{\n"
+	"	gl_FragColor = color;\n"
+	"}\n";
+    cairo_status_t status;
+
+    if (! _cairo_gl_operator_is_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    ctx = _cairo_gl_context_acquire (surface->ctx);
+
+    if (ctx->fill_rectangles_shader == 0) {
+	status = _cairo_gl_load_glsl (&ctx->fill_rectangles_shader,
+				      fill_vs_source, fill_fs_source);
+	if (_cairo_status_is_error (status))
+	    return status;
+
+	ctx->fill_rectangles_color_uniform =
+	    glGetUniformLocationARB (ctx->fill_rectangles_shader, "color");
+    }
+
+    if (num_rects > N_STACK_RECTS) {
+	vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2);
+	if (!vertices) {
+	    _cairo_gl_context_release (ctx);
+	    free (vertices);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+    } else {
+	vertices = vertices_stack;
+    }
+
+    glUseProgramObjectARB (ctx->fill_rectangles_shader);
+
+    _cairo_gl_set_destination (surface);
+    _cairo_gl_set_operator (surface, op);
+
+    gl_color[0] = color->red * color->alpha;
+    gl_color[1] = color->green * color->alpha;
+    gl_color[2] = color->blue * color->alpha;
+    gl_color[3] = color->alpha;
+    glUniform4fvARB (ctx->fill_rectangles_color_uniform, 1, gl_color);
+
+    for (i = 0; i < num_rects; i++) {
+	vertices[i * 8 + 0] = rects[i].x;
+	vertices[i * 8 + 1] = rects[i].y;
+	vertices[i * 8 + 2] = rects[i].x + rects[i].width;
+	vertices[i * 8 + 3] = rects[i].y;
+	vertices[i * 8 + 4] = rects[i].x + rects[i].width;
+	vertices[i * 8 + 5] = rects[i].y + rects[i].height;
+	vertices[i * 8 + 6] = rects[i].x;
+	vertices[i * 8 + 7] = rects[i].y + rects[i].height;
+    }
+
+    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices);
+    glEnableClientState (GL_VERTEX_ARRAY);
+
+    glDrawArrays (GL_QUADS, 0, 4 * num_rects);
+
+    glDisableClientState (GL_VERTEX_ARRAY);
+    glDisable (GL_BLEND);
+    glUseProgramObjectARB (0);
+
+    _cairo_gl_context_release (ctx);
+    if (vertices != vertices_stack)
+	free (vertices);
+
+    return CAIRO_STATUS_SUCCESS;
+#undef N_STACK_RECTS
+}
+
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
+				   cairo_operator_t	    op,
+				   const cairo_color_t     *color,
+				   cairo_rectangle_int_t   *rects,
+				   int			    num_rects)
+{
+    if (GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader) {
+	return _cairo_gl_surface_fill_rectangles_glsl(abstract_surface,
+						      op,
+						      color,
+						      rects,
+						      num_rects);
+    } else {
+	return _cairo_gl_surface_fill_rectangles_fixed(abstract_surface,
+						       op,
+						       color,
+						       rects,
+						       num_rects);
+    }
+}
+
 typedef struct _cairo_gl_surface_span_renderer {
     cairo_span_renderer_t base;
 
-- 
1.6.5.7



More information about the cairo mailing list