summaryrefslogtreecommitdiff
path: root/libs/cairo/src/cairo-gl-shaders.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cairo/src/cairo-gl-shaders.c')
-rw-r--r--libs/cairo/src/cairo-gl-shaders.c960
1 files changed, 960 insertions, 0 deletions
diff --git a/libs/cairo/src/cairo-gl-shaders.c b/libs/cairo/src/cairo-gl-shaders.c
new file mode 100644
index 000000000..d0edffa88
--- /dev/null
+++ b/libs/cairo/src/cairo-gl-shaders.c
@@ -0,0 +1,960 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef struct cairo_gl_shader_impl {
+ void
+ (*compile_shader) (GLuint *shader, GLenum type, const char *text);
+
+ void
+ (*link_shader) (GLuint *program, GLuint vert, GLuint frag);
+
+ void
+ (*destroy_shader) (GLuint shader);
+
+ void
+ (*destroy_program) (GLuint program);
+
+ void
+ (*bind_float) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value);
+
+ void
+ (*bind_vec2) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1);
+
+ void
+ (*bind_vec3) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2);
+
+ void
+ (*bind_vec4) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0, float value1,
+ float value2, float value3);
+
+ void
+ (*bind_matrix) (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_matrix_t* m);
+
+ void
+ (*bind_texture) (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_gl_tex_t tex_unit);
+
+ void
+ (*use) (cairo_gl_shader_t *shader);
+} shader_impl_t;
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ const char *fragment_text);
+
+/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions
+ API. */
+static void
+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);
+ printf ("OpenGL shader compilation failed. Shader:\n"
+ "%s\n"
+ "OpenGL compilation log:\n"
+ "%s\n",
+ text, log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader compilation failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+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);
+ printf ("OpenGL shader link failed:\n%s\n", log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader link failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+destroy_shader_arb (GLuint shader)
+{
+ glDeleteObjectARB (shader);
+}
+
+static void
+destroy_program_arb (GLuint shader)
+{
+ glDeleteObjectARB (shader);
+}
+
+static void
+bind_float_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform1fARB (location, value);
+}
+
+static void
+bind_vec2_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform2fARB (location, value0, value1);
+}
+
+static void
+bind_vec3_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform3fARB (location, value0, value1, value2);
+}
+
+static void
+bind_vec4_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform4fARB (location, value0, value1, value2, value3);
+}
+
+static void
+bind_matrix_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_matrix_t* m)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ float gl_m[9] = {
+ m->xx, m->xy, m->x0,
+ m->yx, m->yy, m->y0,
+ 0, 0, 1
+ };
+ assert (location != -1);
+ glUniformMatrix3fvARB (location, 1, GL_TRUE, gl_m);
+}
+
+static void
+bind_texture_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_gl_tex_t tex_unit)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform1iARB (location, tex_unit);
+}
+
+static void
+use_program_arb (cairo_gl_shader_t *shader)
+{
+ if (shader)
+ glUseProgramObjectARB (shader->program);
+ else
+ glUseProgramObjectARB (0);
+}
+
+/* OpenGL Core 2.0 API. */
+static void
+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);
+ printf ("OpenGL shader compilation failed. Shader:\n"
+ "%s\n"
+ "OpenGL compilation log:\n"
+ "%s\n",
+ text, log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader compilation failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+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);
+ printf ("OpenGL shader link failed:\n%s\n", log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader link failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+destroy_shader_core_2_0 (GLuint shader)
+{
+ glDeleteShader (shader);
+}
+
+static void
+destroy_program_core_2_0 (GLuint shader)
+{
+ glDeleteProgram (shader);
+}
+
+static void
+bind_float_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform1f (location, value);
+}
+
+static void
+bind_vec2_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform2f (location, value0, value1);
+}
+
+static void
+bind_vec3_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform3f (location, value0, value1, value2);
+}
+
+static void
+bind_vec4_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform4f (location, value0, value1, value2, value3);
+}
+
+static void
+bind_matrix_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_matrix_t* m)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ float gl_m[16] = {
+ m->xx, m->xy, m->x0,
+ m->yx, m->yy, m->y0,
+ 0, 0, 1
+ };
+ assert (location != -1);
+ glUniformMatrix3fv (location, 1, GL_TRUE, gl_m);
+}
+
+static void
+bind_texture_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_gl_tex_t tex_unit)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform1i (location, tex_unit);
+}
+
+static void
+use_program_core_2_0 (cairo_gl_shader_t *shader)
+{
+ if (shader)
+ glUseProgram (shader->program);
+ else
+ glUseProgram (0);
+}
+
+static const cairo_gl_shader_impl_t shader_impl_core_2_0 = {
+ compile_shader_core_2_0,
+ link_shader_core_2_0,
+ destroy_shader_core_2_0,
+ destroy_program_core_2_0,
+ bind_float_core_2_0,
+ bind_vec2_core_2_0,
+ bind_vec3_core_2_0,
+ bind_vec4_core_2_0,
+ bind_matrix_core_2_0,
+ bind_texture_core_2_0,
+ use_program_core_2_0,
+};
+
+static const cairo_gl_shader_impl_t shader_impl_arb = {
+ compile_shader_arb,
+ link_shader_arb,
+ destroy_shader_arb,
+ destroy_program_arb,
+ bind_float_arb,
+ bind_vec2_arb,
+ bind_vec3_arb,
+ bind_vec4_arb,
+ bind_matrix_arb,
+ bind_texture_arb,
+ use_program_arb,
+};
+
+typedef struct _cairo_shader_cache_entry {
+ cairo_cache_entry_t base;
+
+ cairo_gl_operand_type_t src;
+ cairo_gl_operand_type_t mask;
+ cairo_gl_operand_type_t dest;
+ cairo_gl_shader_in_t in;
+
+ cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
+ cairo_gl_shader_t shader;
+} cairo_shader_cache_entry_t;
+
+static cairo_bool_t
+_cairo_gl_shader_cache_equal (const void *key_a, const void *key_b)
+{
+ const cairo_shader_cache_entry_t *a = key_a;
+ const cairo_shader_cache_entry_t *b = key_b;
+
+ return a->src == b->src &&
+ a->mask == b->mask &&
+ a->dest == b->dest &&
+ a->in == b->in;
+}
+
+static unsigned long
+_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
+{
+ return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in);
+}
+
+static void
+_cairo_gl_shader_cache_destroy (void *data)
+{
+ cairo_shader_cache_entry_t *entry = data;
+
+ _cairo_gl_shader_fini (entry->ctx, &entry->shader);
+ if (entry->ctx->current_shader == &entry->shader)
+ entry->ctx->current_shader = NULL;
+ free (entry);
+}
+
+static void
+_cairo_gl_shader_init (cairo_gl_shader_t *shader)
+{
+ shader->fragment_shader = 0;
+ shader->program = 0;
+}
+
+cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
+{
+ static const char *fill_fs_source =
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ cairo_status_t status;
+
+ /* XXX multiple device support? */
+ if (GLEW_VERSION_2_0) {
+ ctx->shader_impl = &shader_impl_core_2_0;
+ } else if (GLEW_ARB_shader_objects &&
+ GLEW_ARB_fragment_shader &&
+ GLEW_ARB_vertex_program) {
+ ctx->shader_impl = &shader_impl_arb;
+ } else {
+ ctx->shader_impl = NULL;
+ }
+
+ memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
+
+ status = _cairo_cache_init (&ctx->shaders,
+ _cairo_gl_shader_cache_equal,
+ NULL,
+ _cairo_gl_shader_cache_destroy,
+ CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
+ if (unlikely (status))
+ return status;
+
+ if (ctx->shader_impl != NULL) {
+ _cairo_gl_shader_init (&ctx->fill_rectangles_shader);
+ status = _cairo_gl_shader_compile (ctx,
+ &ctx->fill_rectangles_shader,
+ CAIRO_GL_VAR_NONE,
+ CAIRO_GL_VAR_NONE,
+ fill_fs_source);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
+{
+ int i;
+
+ for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) {
+ if (ctx->vertex_shaders[i])
+ ctx->shader_impl->destroy_shader (ctx->vertex_shaders[i]);
+ }
+
+ _cairo_cache_fini (&ctx->shaders);
+}
+
+void
+_cairo_gl_shader_fini (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ if (shader->fragment_shader)
+ ctx->shader_impl->destroy_shader (shader->fragment_shader);
+
+ if (shader->program)
+ ctx->shader_impl->destroy_program (shader->program);
+}
+
+static const char *operand_names[] = { "source", "mask", "dest" };
+
+static cairo_gl_var_type_t
+cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type)
+{
+ switch (type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT:
+ return CAIRO_GL_VAR_NONE;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ return CAIRO_GL_VAR_TEXCOORDS;
+ case CAIRO_GL_OPERAND_SPANS:
+ return CAIRO_GL_VAR_COVERAGE;
+ }
+}
+
+static void
+cairo_gl_shader_emit_variable (cairo_output_stream_t *stream,
+ cairo_gl_var_type_t type,
+ cairo_gl_tex_t name)
+{
+ switch (type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_VAR_NONE:
+ break;
+ case CAIRO_GL_VAR_TEXCOORDS:
+ _cairo_output_stream_printf (stream,
+ "varying vec2 %s_texcoords;\n",
+ operand_names[name]);
+ break;
+ case CAIRO_GL_VAR_COVERAGE:
+ _cairo_output_stream_printf (stream,
+ "varying float %s_coverage;\n",
+ operand_names[name]);
+ break;
+ }
+}
+
+static void
+cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream,
+ cairo_gl_var_type_t type,
+ cairo_gl_tex_t name)
+{
+ switch (type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_VAR_NONE:
+ break;
+ case CAIRO_GL_VAR_TEXCOORDS:
+ _cairo_output_stream_printf (stream,
+ " %s_texcoords = gl_MultiTexCoord%d.xy;\n",
+ operand_names[name], name);
+ break;
+ case CAIRO_GL_VAR_COVERAGE:
+ _cairo_output_stream_printf (stream,
+ " %s_coverage = gl_Color.a;\n",
+ operand_names[name]);
+ break;
+ }
+}
+
+static cairo_status_t
+cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ cairo_gl_var_type_t dest,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned int length;
+ cairo_status_t status;
+
+ cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_printf (stream,
+ "void main()\n"
+ "{\n"
+ " gl_Position = ftransform();\n");
+
+ cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_write (stream,
+ "}\n\0", 3);
+
+ status = _cairo_memory_stream_destroy (stream, &source, &length);
+ if (unlikely (status))
+ return status;
+
+ *out = (char *) source;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
+ GLuint tex_target,
+ cairo_gl_operand_type_t type,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+ const char *rectstr = (tex_target == GL_TEXTURE_RECTANGLE_EXT ? "Rect" : "");
+
+ switch (type) {
+ case CAIRO_GL_OPERAND_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_GL_OPERAND_NONE:
+ _cairo_output_stream_printf (stream,
+ "vec4 get_%s()\n"
+ "{\n"
+ " return vec4 (0, 0, 0, 1);\n"
+ "}\n",
+ namestr);
+ break;
+ case CAIRO_GL_OPERAND_CONSTANT:
+ _cairo_output_stream_printf (stream,
+ "uniform vec4 %s_constant;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return %s_constant;\n"
+ "}\n",
+ namestr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler2D%s %s_sampler;\n"
+ "varying vec2 %s_texcoords;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return texture2D%s(%s_sampler, %s_texcoords);\n"
+ "}\n",
+ rectstr, namestr, namestr, namestr, rectstr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler1D %s_sampler;\n"
+ "uniform mat3 %s_matrix;\n"
+ "uniform vec2 %s_segment;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n"
+ " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n"
+ " return texture1D (%s_sampler, t);\n"
+ "}\n",
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler1D %s_sampler;\n"
+ "uniform mat3 %s_matrix;\n"
+ "uniform vec2 %s_circle_1;\n"
+ "uniform float %s_radius_0;\n"
+ "uniform float %s_radius_1;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n"
+ " \n"
+ " float dr = %s_radius_1 - %s_radius_0;\n"
+ " float dot_circle_1 = dot (%s_circle_1, %s_circle_1);\n"
+ " float dot_pos_circle_1 = dot (pos, %s_circle_1);\n"
+ " \n"
+ " float A = dot_circle_1 - dr * dr;\n"
+ " float B = -2.0 * (dot_pos_circle_1 + %s_radius_0 * dr);\n"
+ " float C = dot (pos, pos) - %s_radius_0 * %s_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"
+ " sqrt_det *= sign(A);\n"
+ " \n"
+ " float t = (-B + sqrt_det) / (2.0 * A);\n"
+ " return texture1D (%s_sampler, t);\n"
+ "}\n",
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr,
+ namestr);
+ break;
+ case CAIRO_GL_OPERAND_SPANS:
+ _cairo_output_stream_printf (stream,
+ "varying float %s_coverage;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return vec4(0, 0, 0, %s_coverage);\n"
+ "}\n",
+ namestr, namestr, namestr);
+ break;
+ }
+}
+
+static cairo_status_t
+cairo_gl_shader_get_fragment_source (GLuint tex_target,
+ cairo_gl_shader_in_t in,
+ cairo_gl_operand_type_t src,
+ cairo_gl_operand_type_t mask,
+ cairo_gl_operand_type_t dest,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned int length;
+ cairo_status_t status;
+
+ cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_printf (stream,
+ "void main()\n"
+ "{\n");
+ switch (in) {
+ case CAIRO_GL_SHADER_IN_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_SHADER_IN_NORMAL:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask().a;\n");
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask();\n");
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source().a * get_mask();\n");
+ break;
+ }
+
+ _cairo_output_stream_write (stream,
+ "}\n\0", 3);
+
+ status = _cairo_memory_stream_destroy (stream, &source, &length);
+ if (unlikely (status))
+ return status;
+
+ *out = (char *) source;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ const char *fragment_text)
+{
+ unsigned int vertex_shader;
+ cairo_status_t status;
+
+ if (ctx->shader_impl == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (shader->program == 0);
+
+ vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE);
+ if (ctx->vertex_shaders[vertex_shader] == 0) {
+ char *source;
+
+ status = cairo_gl_shader_get_vertex_source (src,
+ mask,
+ CAIRO_GL_VAR_NONE,
+ &source);
+ if (unlikely (status))
+ goto FAILURE;
+
+ ctx->shader_impl->compile_shader (&ctx->vertex_shaders[vertex_shader],
+ GL_VERTEX_SHADER,
+ source);
+ free (source);
+ }
+
+ ctx->shader_impl->compile_shader (&shader->fragment_shader,
+ GL_FRAGMENT_SHADER,
+ fragment_text);
+
+ ctx->shader_impl->link_shader (&shader->program,
+ ctx->vertex_shaders[vertex_shader],
+ shader->fragment_shader);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAILURE:
+ _cairo_gl_shader_fini (ctx, shader);
+ shader->fragment_shader = 0;
+ shader->program = 0;
+
+ return status;
+}
+
+void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+ const char *name,
+ float value)
+{
+ ctx->shader_impl->bind_float (ctx->current_shader, name, value);
+}
+
+void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0,
+ float value1)
+{
+ ctx->shader_impl->bind_vec2 (ctx->current_shader, name, value0, value1);
+}
+
+void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ ctx->shader_impl->bind_vec3 (ctx->current_shader, name, value0, value1, value2);
+}
+
+void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0, float value1,
+ float value2, float value3)
+{
+ ctx->shader_impl->bind_vec4 (ctx->current_shader, name, value0, value1, value2, value3);
+}
+
+void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+ const char *name, cairo_matrix_t* m)
+{
+ ctx->shader_impl->bind_matrix (ctx->current_shader, name, m);
+}
+
+void
+_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx,
+ const char *name, GLuint tex_unit)
+{
+ ctx->shader_impl->bind_texture (ctx->current_shader, name, tex_unit);
+}
+
+void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ if (ctx->shader_impl == NULL)
+ return;
+
+ if (ctx->current_shader == shader)
+ return;
+
+ ctx->shader_impl->use (shader);
+
+ ctx->current_shader = shader;
+}
+
+cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+ cairo_gl_operand_type_t source,
+ cairo_gl_operand_type_t mask,
+ cairo_gl_shader_in_t in,
+ cairo_gl_shader_t **shader)
+{
+ cairo_shader_cache_entry_t lookup, *entry;
+ char *fs_source;
+ cairo_status_t status;
+
+ if (ctx->shader_impl == NULL) {
+ *shader = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ lookup.src = source;
+ lookup.mask = mask;
+ lookup.dest = CAIRO_GL_OPERAND_NONE;
+ lookup.in = in;
+ lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup);
+ lookup.base.size = 1;
+
+ entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base);
+ if (entry) {
+ assert (entry->shader.program);
+ *shader = &entry->shader;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = cairo_gl_shader_get_fragment_source (ctx->tex_target,
+ in,
+ source,
+ mask,
+ CAIRO_GL_OPERAND_NONE,
+ &fs_source);
+ if (unlikely (status))
+ return status;
+
+ entry = malloc (sizeof (cairo_shader_cache_entry_t));
+ if (unlikely (entry == NULL)) {
+ free (fs_source);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t));
+
+ entry->ctx = ctx;
+ _cairo_gl_shader_init (&entry->shader);
+ status = _cairo_gl_shader_compile (ctx,
+ &entry->shader,
+ cairo_gl_operand_get_var_type (source),
+ cairo_gl_operand_get_var_type (mask),
+ fs_source);
+ free (fs_source);
+
+ if (unlikely (status)) {
+ free (entry);
+ return status;
+ }
+
+ status = _cairo_cache_insert (&ctx->shaders, &entry->base);
+ if (unlikely (status)) {
+ _cairo_gl_shader_fini (ctx, &entry->shader);
+ free (entry);
+ return status;
+ }
+
+ *shader = &entry->shader;
+
+ return CAIRO_STATUS_SUCCESS;
+}