[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