diff options
Diffstat (limited to 'libs/cairo/src/cairo-gl-shaders.c')
-rw-r--r-- | libs/cairo/src/cairo-gl-shaders.c | 960 |
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; +} |