cairo-gl-shaders.c [plain text]
#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);
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);
}
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;
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;
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 long 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 long 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;
}