[cairo] [PATCH] [gl] Add shader support code for GL versions < 3.0.

Zach Laine whatwasthataddress at gmail.com
Thu Jan 14 15:01:09 PST 2010


On Thu, Jan 14, 2010 at 12:28 PM, Chris Wilson <chris at chris-wilson.co.uk> wrote:
> On Thu, 14 Jan 2010 11:49:04 -0600, Zach Laine <whatwasthataddress at gmail.com> wrote:
>> Adds cairo_gl_shader_program_t, and functions to manipulate same.  Multiple GL
>> entry points for shaders are provided -- one for the pre-GL 2.0 extenstions
>> entry points, and one for GL 2.0.  This code is well tested, but currently
>> unused in the GL backend.
>
> Hi Zach, this looks good and is definitely the direction we want to be
> going in, but... ;-)

Isn't that always the way ... :)

> I've some specific comments on style inline, but first let's look at the
> general design. cairo-gl is based around the idea of classifying the
> operation using its cairo_gl_composite_setup_t. This may contain 4
> channels, not just source, but mask, clipmask and possibly a copy of the
> destination as a texture (all depending on the operation) and a program
> has to be compiled to execute that in one or more passes (depending on
> hardware and available resources). In that represent, I feel the fixed
> programs are too simplistic. For example, look at the approach taken by
> glitz which pre-compiles all possible combinations of source and masks,
> and that is still just a small fraction of the number of programs we will
> need to run, for example, WebKit without falling back to a multi-pass
> composite. So the interface that I'd like to see is where we can pass in a
> cairo_gl_composite_setup_t (or whatever) and have the shader manager
> select the right program (and caching the results). The best example of
> how I think this should look is:
>  http://cgit.freedesktop.org/~ickle/cairo/tree/src/drm/cairo-drm-i965-shader.c?h=wip/compositor
> or
>  http://cgit.freedesktop.org/~ickle/cairo/tree/src/drm/cairo-drm-i915-shader.c?h=wip/compositor
>
> Eric will be able to point to some more apt examples for GL, in
> particular his glamor includes a shader manager and has a very similar job
> to the gl-compositor.

Ok.  But the existence of more complicated cases don't mean that
simpler cases shouldn't be implemented by simpler shaders, right?

Note, you just posted a very specific shader in your recent patch:

> +    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";

And I thinks that's completely appropriate.  The alternative is to use
a more complicated shader for the simpler cases, and either initialize
the uniforms that the simpler case doesn't need with safe defaults, or
add conditional logic to the shader, both of which have performance
implications.

[snip]

I implemented the other changes, and added cairo_private to the header
declarations, as suggested by make check.  Thanks for pointing me to
that!
===============
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 1e04ed2..b94516f 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-shader.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..9122d84 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -34,6 +34,7 @@
  * Contributor(s):
  *	Carl Worth <cworth at cworth.org>
  *	Chris Wilson <chris at chris-wilson.co.uk>
+ *      T. Zachary Laine <whatwasthataddress at gmail.com>
  */

 #ifndef CAIRO_GL_PRIVATE_H
@@ -79,6 +80,13 @@ typedef struct cairo_gl_glyph_cache {
     unsigned int width, height;
 } cairo_gl_glyph_cache_t;

+typedef struct cairo_gl_shader_program {
+    GLuint vertex_shader;
+    GLuint fragment_shader;
+    GLuint program;
+    cairo_bool_t build_failure;
+} cairo_gl_shader_program_t;
+
 struct _cairo_gl_context {
     cairo_reference_count_t ref_count;
     cairo_status_t status;
@@ -211,6 +219,47 @@ _cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
 	return (surface->height - 1) - y;
 }

+cairo_private void
+_cairo_gl_shader_program_init (cairo_gl_shader_program_t *program);
+
+cairo_private void
+_cairo_gl_shader_program_destroy (cairo_gl_shader_program_t *program);
+
+cairo_private cairo_bool_t
+_cairo_gl_shader_program_create (cairo_gl_shader_program_t *program,
+                                 const char *vertex_text,
+                                 const char *fragment_text);
+
+cairo_private cairo_bool_t
+_cairo_gl_shader_program_create_linear_gradient
(cairo_gl_shader_program_t *program);
+
+cairo_private cairo_bool_t
+_cairo_gl_shader_program_create_radial_gradient
(cairo_gl_shader_program_t *program);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_float_to_shader (GLuint program, const char *name,
+                                float value);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_vec2_to_shader (GLuint program, const char *name,
+                               float value0, float value1);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_vec3_to_shader (GLuint program, const char *name,
+                               float value0, float value1,
+                               float value2);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_vec4_to_shader (GLuint program, const char *name,
+                               float value0, float value1,
+                               float value2, float value3);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_matrix_to_shader (GLuint program, const char *name,
cairo_matrix_t* m);
+
+cairo_private cairo_bool_t
+_cairo_gl_bind_texture_to_shader (GLuint program, const char *name,
GLuint tex_unit);
+
 slim_hidden_proto (cairo_gl_surface_create);

 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shader.c b/src/cairo-gl-shader.c
new file mode 100644
index 0000000..66e10c0
--- /dev/null
+++ b/src/cairo-gl-shader.c
@@ -0,0 +1,652 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 T. Zachary Laine
+ *
+ * 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 T. Zachary Laine.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+typedef struct _shader_impl {
+    cairo_bool_t
+    (*compile_shader) (GLuint *shader, GLenum type, const char *text);
+
+    cairo_bool_t
+    (*link_shader) (GLuint *program, GLuint vert, GLuint frag);
+
+    void
+    (*destroy_shader_program) (cairo_gl_shader_program_t *program);
+
+    cairo_bool_t
+    (*create_linear_gradient_shader_program)
(cairo_gl_shader_program_t *program);
+
+    cairo_bool_t
+    (*create_radial_gradient_shader_program)
(cairo_gl_shader_program_t *program);
+
+    cairo_bool_t
+    (*bind_float_to_shader) (GLuint program, const char *name,
+                             float value);
+
+    cairo_bool_t
+    (*bind_vec2_to_shader) (GLuint program, const char *name,
+                            float value0, float value1);
+
+    cairo_bool_t
+    (*bind_vec3_to_shader) (GLuint program, const char *name,
+                            float value0, float value1,
+                            float value2);
+
+    cairo_bool_t
+    (*bind_vec4_to_shader) (GLuint program, const char *name,
+                            float value0, float value1,
+                            float value2, float value3);
+
+    cairo_bool_t
+    (*bind_matrix_to_shader) (GLuint program, const char *name,
cairo_matrix_t* m);
+
+    cairo_bool_t
+    (*bind_texture_to_shader) (GLuint program, const char *name,
GLuint tex_unit);
+
+    GLenum
+    (*vertex_enumerator) (void);
+
+    GLenum
+    (*fragment_enumerator) (void);
+} shader_impl_t;
+
+static const shader_impl_t*
+get_impl (void);
+
+static const char * const minimal_vert_text_110 =
+    "#version 110\n"
+    "\n"
+    "void main ()\n"
+    "{ gl_Position = ftransform(); }\n";
+
+/* This fragment shader was adapted from Argiris Kirtzidis' cairo-gral
+ * library, found at git://github.com/akyrtzi/cairo-gral.git.  Argiris' shader
+ * was adapted from the original algorithm in pixman.
+ */
+static const char * const radial_gradient_frag_text_110 =
+    "#version 110\n"
+    "\n"
+    "uniform sampler1D tex;\n"
+    "uniform mat4 matrix;\n"
+    "uniform vec2 circle_1;\n"
+    "uniform float radius_0;\n"
+    "uniform float radius_1;\n"
+    "uniform float first_offset;\n"
+    "uniform float last_offset;\n"
+    "\n"
+    "void main ()\n"
+    "{\n"
+    "    vec2 pos = (matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    \n"
+    "    float dr = radius_1 - radius_0;\n"
+    "    float dot_circle_1 = dot (circle_1, circle_1);\n"
+    "    float dot_pos_circle_1 = dot (pos, circle_1);\n"
+    "    \n"
+    "    float A = dot_circle_1 - dr * dr;\n"
+    "    float B = -2.0 * (dot_pos_circle_1 + radius_0 * dr);\n"
+    "    float C = dot (pos, pos) - radius_0 * radius_0;\n"
+    "    float det = B * B - 4.0 * A * C;\n"
+    "    det = max (det, 0.0);\n"
+    "    \n"
+    "    float sqrt_det = sqrt (det);\n"
+    "    /* This complicated bit of logic acts as\n"
+    "     * \"if (A < 0.0) sqrt_det = -sqrt_det\", without the branch.\n"
+    "     */\n"
+    "    sqrt_det *= 1.0 + 2.0 * sign (min (A, 0.0));\n"
+    "    \n"
+    "    float t = (-B + sqrt_det) / (2.0 * A);\n"
+    "    t = (t - first_offset) / (last_offset - first_offset);\n"
+    "    gl_FragColor = texture1D (tex, t);\n"
+    "}\n";
+
+static const char * const linear_gradient_frag_text_110 =
+    "#version 110\n"
+    "\n"
+    "uniform sampler1D tex;\n"
+    "uniform mat4 matrix;\n"
+    "uniform vec2 segment;\n"
+    "uniform float first_offset;\n"
+    "uniform float last_offset;\n"
+    "\n"
+    "void main ()\n"
+    "{\n"
+    "    vec2 pos = (matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    float t = dot (pos, segment) / dot (segment, segment);\n"
+    "    t = (t - first_offset) / (last_offset - first_offset);\n"
+    "    gl_FragColor = texture1D (tex, t);\n"
+    "}\n";
+
+/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions
+   API. */
+static cairo_bool_t
+compile_shader_arb (GLuint *shader, GLenum type, const char *text)
+{
+    const char* strings[1] = { text };
+    GLint gl_status;
+
+    *shader = glCreateShaderObjectARB (type);
+    glShaderSourceARB (*shader, 1, strings, 0);
+    glCompileShaderARB (*shader);
+    glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB,
&gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetObjectParameterivARB (*shader,
GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetInfoLogARB (*shader, log_size, &chars, log);
+            fprintf (stderr,
+                     "OpenGL shader compilation failed.  Shader:\n"
+                     "%s\n"
+                     "OpenGL compilation log:\n"
+                     "%s\n",
+                     text, log);
+
+            free (log);
+        } else {
+            fprintf (stderr, "OpenGL shader compilation failed.\n");
+        }
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+link_shader_arb (GLuint *program, GLuint vert, GLuint frag)
+{
+    GLint gl_status;
+
+    *program = glCreateProgramObjectARB ();
+    glAttachObjectARB (*program, vert);
+    glAttachObjectARB (*program, frag);
+    glLinkProgramARB (*program);
+    glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB,
&gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetObjectParameterivARB (*program,
GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetInfoLogARB (*program, log_size, &chars, log);
+            fprintf (stderr, "OpenGL shader link failed:\n%s\n", log);
+
+            free (log);
+        } else {
+            fprintf (stderr, "OpenGL shader link failed.\n");
+        }
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+destroy_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    if (program->vertex_shader)
+        glDeleteObjectARB (program->vertex_shader);
+    if (program->fragment_shader)
+        glDeleteObjectARB (program->fragment_shader);
+    if (program->program)
+        glDeleteObjectARB (program->program);
+}
+
+static cairo_bool_t
+create_linear_gradient_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    return _cairo_gl_shader_program_create (program,
+                                            minimal_vert_text_110,
+                                            linear_gradient_frag_text_110);
+}
+
+static cairo_bool_t
+create_radial_gradient_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    return _cairo_gl_shader_program_create (program,
+                                            minimal_vert_text_110,
+                                            radial_gradient_frag_text_110);
+}
+
+static cairo_bool_t
+bind_float_to_shader_arb (GLuint program, const char *name,
+                               float value)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform1fARB (location, value);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec2_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform2fARB (location, value0, value1);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec3_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform3fARB (location, value0, value1, value2);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec4_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2, float value3)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform4fARB (location, value0, value1, value2, value3);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_matrix_to_shader_arb (GLuint program, const char *name, cairo_matrix_t* m)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    float gl_m[16] = {
+        m->xx, m->xy, 0,     m->x0,
+        m->yx, m->yy, 0,     m->y0,
+        0,     0,     1,     0,
+        0,     0,     0,     1
+    };
+    glUniformMatrix4fvARB (location, 1, GL_TRUE, gl_m);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform1iARB (location, tex_unit);
+    return TRUE;
+}
+
+static GLenum
+vertex_enumerator_arb (void)
+{
+    return GL_VERTEX_SHADER_ARB;
+}
+
+static GLenum
+fragment_enumerator_arb (void)
+{
+    return GL_FRAGMENT_SHADER_ARB;
+}
+
+/* OpenGL Core 2.0 API. */
+static cairo_bool_t
+compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text)
+{
+    const char* strings[1] = { text };
+    GLint gl_status;
+
+    *shader = glCreateShader (type);
+    glShaderSource (*shader, 1, strings, 0);
+    glCompileShader (*shader);
+    glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetShaderInfoLog (*shader, log_size, &chars, log);
+            fprintf (stderr,
+                     "OpenGL shader compilation failed.  Shader:\n"
+                     "%s\n"
+                     "OpenGL compilation log:\n"
+                    "%s\n",
+                     text, log);
+
+            free (log);
+        } else {
+            fprintf (stderr, "OpenGL shader compilation failed.\n");
+        }
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static cairo_bool_t
+link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag)
+{
+    GLint gl_status;
+
+    *program = glCreateProgram ();
+    glAttachShader (*program, vert);
+    glAttachShader (*program, frag);
+    glLinkProgram (*program);
+    glGetProgramiv (*program, GL_LINK_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetProgramInfoLog (*program, log_size, &chars, log);
+            fprintf (stderr, "OpenGL shader link failed:\n%s\n", log);
+
+            free (log);
+        } else {
+            fprintf (stderr, "OpenGL shader link failed.\n");
+        }
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+destroy_shader_program_core_2_0 (cairo_gl_shader_program_t *program)
+{
+    glDeleteShader (program->vertex_shader);
+    glDeleteShader (program->fragment_shader);
+    glDeleteProgram (program->program);
+}
+
+static cairo_bool_t
+create_linear_gradient_shader_program_core_2_0
(cairo_gl_shader_program_t *program)
+{
+    return _cairo_gl_shader_program_create (program,
+                                            minimal_vert_text_110,
+                                            linear_gradient_frag_text_110);
+}
+
+static cairo_bool_t
+create_radial_gradient_shader_program_core_2_0
(cairo_gl_shader_program_t *program)
+{
+    return _cairo_gl_shader_program_create (program,
+                                            minimal_vert_text_110,
+                                            radial_gradient_frag_text_110);
+}
+
+static cairo_bool_t
+bind_float_to_shader_core_2_0 (GLuint program, const char *name,
+                               float value)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform1f (location, value);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec2_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform2f (location, value0, value1);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec3_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform3f (location, value0, value1, value2);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_vec4_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2, float value3)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform4f (location, value0, value1, value2, value3);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_matrix_to_shader_core_2_0 (GLuint program, const char *name,
cairo_matrix_t* m)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    float gl_m[16] = {
+        m->xx, m->xy, 0,     m->x0,
+        m->yx, m->yy, 0,     m->y0,
+        0,     0,     1,     0,
+        0,     0,     0,     1
+    };
+    glUniformMatrix4fv (location, 1, GL_TRUE, gl_m);
+    return TRUE;
+}
+
+static cairo_bool_t
+bind_texture_to_shader_core_2_0 (GLuint program, const char *name,
GLuint tex_unit)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return FALSE;
+    glUniform1i (location, tex_unit);
+    return TRUE;
+}
+
+static GLenum
+vertex_enumerator_core_2_0 (void)
+{
+    return GL_VERTEX_SHADER;
+}
+
+static GLenum
+fragment_enumerator_core_2_0 (void)
+{
+    return GL_FRAGMENT_SHADER;
+}
+
+#define SHADER_IMPL_DECL(x)                                             \
+    static const shader_impl_t shader_impl_ ## x = {                    \
+        compile_shader_ ## x,                                           \
+        link_shader_ ## x,                                              \
+        destroy_shader_program_ ## x,                                   \
+        create_linear_gradient_shader_program_ ## x,                    \
+        create_radial_gradient_shader_program_ ## x,                    \
+        bind_float_to_shader_ ## x,                                     \
+        bind_vec2_to_shader_ ## x,                                      \
+        bind_vec3_to_shader_ ## x,                                      \
+        bind_vec4_to_shader_ ## x,                                      \
+        bind_matrix_to_shader_ ## x,                                    \
+        bind_texture_to_shader_ ## x,                                   \
+        vertex_enumerator_ ## x,                                        \
+        fragment_enumerator_ ## x                                       \
+    }
+
+SHADER_IMPL_DECL(core_2_0);
+SHADER_IMPL_DECL(arb);
+
+#undef SHADER_IMPL_DECL
+
+static const shader_impl_t*
+get_impl (void)
+{
+    if (GLEW_VERSION_2_0) {
+        return &shader_impl_core_2_0;
+    } else if (GLEW_ARB_shader_objects &&
+               GLEW_ARB_fragment_shader &&
+               GLEW_ARB_vertex_program) {
+        return &shader_impl_arb;
+    }
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+void
+_cairo_gl_shader_program_init (cairo_gl_shader_program_t *program)
+{
+    program->vertex_shader = 0;
+    program->fragment_shader = 0;
+    program->program = 0;
+    program->build_failure = FALSE;
+}
+
+void
+_cairo_gl_shader_program_destroy (cairo_gl_shader_program_t *program)
+{
+    return get_impl()->destroy_shader_program(program);
+}
+
+cairo_bool_t
+_cairo_gl_shader_program_create (cairo_gl_shader_program_t *program,
+                                 const char *vertex_text,
+                                 const char *fragment_text)
+{
+    if (program->program != 0)
+        return TRUE;
+
+    if (program->build_failure)
+        return FALSE;
+
+    if (! get_impl()->compile_shader (&program->vertex_shader,
+                                      get_impl()->vertex_enumerator(),
+                                      vertex_text))
+        goto FAILURE;
+
+    if (! get_impl()->compile_shader (&program->fragment_shader,
+                                      get_impl()->fragment_enumerator(),
+                                      fragment_text))
+        goto FAILURE;
+
+    if (! get_impl()->link_shader (&program->program,
+                                   program->vertex_shader,
+                                   program->fragment_shader))
+        goto FAILURE;
+
+    return TRUE;
+
+ FAILURE:
+    _cairo_gl_shader_program_destroy (program);
+    program->vertex_shader = 0;
+    program->fragment_shader = 0;
+    program->program = 0;
+    program->build_failure = TRUE;
+
+    return FALSE;
+}
+
+cairo_bool_t
+_cairo_gl_shader_program_create_linear_gradient
(cairo_gl_shader_program_t *program)
+{
+    return get_impl()->create_linear_gradient_shader_program(program);
+}
+
+cairo_bool_t
+_cairo_gl_shader_program_create_radial_gradient
(cairo_gl_shader_program_t *program)
+{
+    return get_impl()->create_radial_gradient_shader_program(program);
+}
+
+cairo_bool_t
+_cairo_gl_bind_float_to_shader (GLuint program, const char *name,
+                                float value)
+{
+    return get_impl()->bind_float_to_shader(program, name, value);
+}
+
+cairo_bool_t
+_cairo_gl_bind_vec2_to_shader (GLuint program, const char *name,
+                               float value0, float value1)
+{
+    return get_impl()->bind_vec2_to_shader(program, name, value0, value1);
+}
+
+cairo_bool_t
+_cairo_gl_bind_vec3_to_shader (GLuint program, const char *name,
+                               float value0, float value1,
+                               float value2)
+{
+    return get_impl()->bind_vec3_to_shader(program, name, value0,
value1, value2);
+}
+
+cairo_bool_t
+_cairo_gl_bind_vec4_to_shader (GLuint program, const char *name,
+                               float value0, float value1,
+                               float value2, float value3)
+{
+    return get_impl()->bind_vec4_to_shader(program, name, value0,
value1, value2, value3);
+}
+
+cairo_bool_t
+_cairo_gl_bind_matrix_to_shader (GLuint program, const char *name,
cairo_matrix_t* m)
+{
+    return get_impl()->bind_matrix_to_shader(program, name, m);
+}
+
+cairo_bool_t
+_cairo_gl_bind_texture_to_shader (GLuint program, const char *name,
GLuint tex_unit)
+{
+    return get_impl()->bind_texture_to_shader(program, name, tex_unit);
+}


More information about the cairo mailing list