gl: Remove the shader language version abstraction

Cairo only needs to support one version of the shader language API,
thanks to the dispatch table. This seems unlikely to change any time
soon. This makes the addition of new features, such as a uniform
location cache, simpler.
This commit is contained in:
Martin Robinson 2012-08-17 16:00:51 -07:00
parent 5c77b4df14
commit 9741099093
2 changed files with 159 additions and 311 deletions

View file

@ -193,8 +193,6 @@ typedef enum cairo_gl_tex {
CAIRO_GL_TEX_TEMP = 2
} cairo_gl_tex_t;
typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t;
typedef struct cairo_gl_shader {
GLuint fragment_shader;
GLuint program;
@ -316,7 +314,7 @@ struct _cairo_gl_context {
cairo_bool_t supports_msaa;
char *vb;
const cairo_gl_shader_impl_t *shader_impl;
cairo_bool_t has_shader_support;
GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1];
cairo_gl_shader_t fill_rectangles_shader;
@ -419,7 +417,7 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
static cairo_always_inline cairo_bool_t
_cairo_gl_device_has_glsl (cairo_device_t *device)
{
return ((cairo_gl_context_t *) device)->shader_impl != NULL;
return ((cairo_gl_context_t *) device)->has_shader_support;
}
static cairo_always_inline cairo_bool_t

View file

@ -44,270 +44,13 @@
#include "cairo-error-private.h"
#include "cairo-output-stream-private.h"
typedef struct cairo_gl_shader_impl {
void
(*compile_shader) (cairo_gl_context_t *ctx, GLuint *shader, GLenum type,
const char *text);
void
(*link_shader) (cairo_gl_context_t *ctx, GLuint *program, GLuint vert, GLuint frag);
void
(*destroy_shader) (cairo_gl_context_t *ctx, GLuint shader);
void
(*destroy_program) (cairo_gl_context_t *ctx, GLuint program);
void
(*bind_float) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value);
void
(*bind_vec2) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0,
float value1);
void
(*bind_vec3) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0,
float value1,
float value2);
void
(*bind_vec4) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0, float value1,
float value2, float value3);
void
(*bind_matrix) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
cairo_matrix_t* m);
void
(*bind_matrix4f) (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
GLfloat* gl_m);
void
(*use) (cairo_gl_context_t *ctx,
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,
cairo_bool_t use_coverage,
const char *fragment_text);
/* OpenGL Core 2.0 API. */
static void
compile_shader_core_2_0 (cairo_gl_context_t *ctx, GLuint *shader,
GLenum type, const char *text)
{
const char* strings[1] = { text };
GLint gl_status;
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
*shader = dispatch->CreateShader (type);
dispatch->ShaderSource (*shader, 1, strings, 0);
dispatch->CompileShader (*shader);
dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status);
if (gl_status == GL_FALSE) {
GLint log_size;
dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
if (0 < log_size) {
char *log = _cairo_malloc (log_size);
GLint chars;
log[log_size - 1] = '\0';
dispatch->GetShaderInfoLog (*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 (cairo_gl_context_t *ctx, GLuint *program,
GLuint vert, GLuint frag)
{
GLint gl_status;
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
*program = dispatch->CreateProgram ();
dispatch->AttachShader (*program, vert);
dispatch->AttachShader (*program, frag);
dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
"Vertex");
dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
"Color");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
"MultiTexCoord0");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
"MultiTexCoord1");
dispatch->LinkProgram (*program);
dispatch->GetProgramiv (*program, GL_LINK_STATUS, &gl_status);
if (gl_status == GL_FALSE) {
GLint log_size;
dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
if (0 < log_size) {
char *log = _cairo_malloc (log_size);
GLint chars;
log[log_size - 1] = '\0';
dispatch->GetProgramInfoLog (*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 (cairo_gl_context_t *ctx, GLuint shader)
{
ctx->dispatch.DeleteShader (shader);
}
static void
destroy_program_core_2_0 (cairo_gl_context_t *ctx, GLuint shader)
{
ctx->dispatch.DeleteProgram (shader);
}
static void
bind_float_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (shader->program, name);
assert (location != -1);
dispatch->Uniform1f (location, value);
}
static void
bind_vec2_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0,
float value1)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (shader->program, name);
assert (location != -1);
dispatch->Uniform2f (location, value0, value1);
}
static void
bind_vec3_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0,
float value1,
float value2)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (shader->program, name);
assert (location != -1);
dispatch->Uniform3f (location, value0, value1, value2);
}
static void
bind_vec4_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
float value0,
float value1,
float value2,
float value3)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (shader->program, name);
assert (location != -1);
dispatch->Uniform4f (location, value0, value1, value2, value3);
}
static void
bind_matrix_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
cairo_matrix_t* m)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (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);
dispatch->UniformMatrix3fv (location, 1, GL_TRUE, gl_m);
}
static void
bind_matrix4f_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
const char *name,
GLfloat* gl_m)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (shader->program, name);
assert (location != -1);
dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
}
static void
use_program_core_2_0 (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader)
{
if (shader)
ctx->dispatch.UseProgram (shader->program);
else
ctx->dispatch.UseProgram (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_matrix4f_core_2_0,
use_program_core_2_0,
};
_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
cairo_gl_var_type_t src,
cairo_gl_var_type_t mask,
cairo_bool_t use_coverage,
const char *fragment_text);
typedef struct _cairo_shader_cache_entry {
cairo_cache_entry_t base;
@ -412,13 +155,10 @@ _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) ||
(_cairo_gl_has_extension ("GL_ARB_shader_objects") &&
_cairo_gl_has_extension ("GL_ARB_fragment_shader") &&
_cairo_gl_has_extension ("GL_ARB_vertex_shader")))
{
ctx->shader_impl = &shader_impl_core_2_0;
}
else
{
ctx->shader_impl = NULL;
_cairo_gl_has_extension ("GL_ARB_vertex_shader"))) {
ctx->has_shader_support = TRUE;
} else {
ctx->has_shader_support = FALSE;
fprintf (stderr, "Error: The cairo gl backend requires shader support!\n");
return CAIRO_STATUS_DEVICE_ERROR;
}
@ -436,12 +176,12 @@ _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
return status;
_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,
FALSE,
fill_fs_source);
status = _cairo_gl_shader_compile_and_link (ctx,
&ctx->fill_rectangles_shader,
CAIRO_GL_VAR_NONE,
CAIRO_GL_VAR_NONE,
FALSE,
fill_fs_source);
if (unlikely (status))
return status;
@ -455,7 +195,7 @@ _cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) {
if (ctx->vertex_shaders[i])
ctx->shader_impl->destroy_shader (ctx, ctx->vertex_shaders[i]);
ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]);
}
_cairo_cache_fini (&ctx->shaders);
@ -466,10 +206,10 @@ _cairo_gl_shader_fini (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader)
{
if (shader->fragment_shader)
ctx->shader_impl->destroy_shader (ctx, shader->fragment_shader);
ctx->dispatch.DeleteShader (shader->fragment_shader);
if (shader->program)
ctx->shader_impl->destroy_program (ctx, shader->program);
ctx->dispatch.DeleteProgram (shader->program);
}
static const char *operand_names[] = { "source", "mask", "dest" };
@ -966,13 +706,93 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx,
return CAIRO_STATUS_SUCCESS;
}
static void
compile_shader (cairo_gl_context_t *ctx,
GLuint *shader,
GLenum type,
const char *source)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint success, log_size, num_chars;
char *log;
*shader = dispatch->CreateShader (type);
dispatch->ShaderSource (*shader, 1, &source, 0);
dispatch->CompileShader (*shader);
dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success);
if (success)
return;
dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
if (log_size < 0) {
printf ("OpenGL shader compilation failed.\n");
ASSERT_NOT_REACHED;
return;
}
log = _cairo_malloc (log_size + 1);
dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log);
log[num_chars] = '\0';
printf ("OpenGL shader compilation failed. Shader:\n%s\n", source);
printf ("OpenGL compilation log:\n%s\n", log);
free (log);
ASSERT_NOT_REACHED;
}
static void
link_shader_program (cairo_gl_context_t *ctx,
GLuint *program,
GLuint vert,
GLuint frag)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint success, log_size, num_chars;
char *log;
*program = dispatch->CreateProgram ();
dispatch->AttachShader (*program, vert);
dispatch->AttachShader (*program, frag);
dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
"Vertex");
dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
"Color");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
"MultiTexCoord0");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
"MultiTexCoord1");
dispatch->LinkProgram (*program);
dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success);
if (success)
return;
dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
if (log_size < 0) {
printf ("OpenGL shader link failed.\n");
ASSERT_NOT_REACHED;
return;
}
log = _cairo_malloc (log_size + 1);
dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log);
log[num_chars] = '\0';
printf ("OpenGL shader link failed:\n%s\n", log);
free (log);
ASSERT_NOT_REACHED;
}
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,
cairo_bool_t use_coverage,
const char *fragment_text)
_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
cairo_gl_var_type_t src,
cairo_gl_var_type_t mask,
cairo_bool_t use_coverage,
const char *fragment_text)
{
unsigned int vertex_shader;
cairo_status_t status;
@ -992,19 +812,17 @@ _cairo_gl_shader_compile (cairo_gl_context_t *ctx,
if (unlikely (status))
goto FAILURE;
ctx->shader_impl->compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
GL_VERTEX_SHADER,
source);
compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
GL_VERTEX_SHADER, source);
free (source);
}
ctx->shader_impl->compile_shader (ctx, &shader->fragment_shader,
GL_FRAGMENT_SHADER,
fragment_text);
compile_shader (ctx, &shader->fragment_shader,
GL_FRAGMENT_SHADER, fragment_text);
ctx->shader_impl->link_shader (ctx, &shader->program,
ctx->vertex_shaders[vertex_shader],
shader->fragment_shader);
link_shader_program (ctx, &shader->program,
ctx->vertex_shaders[vertex_shader],
shader->fragment_shader);
return CAIRO_STATUS_SUCCESS;
@ -1053,7 +871,11 @@ _cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
const char *name,
float value)
{
ctx->shader_impl->bind_float (ctx, ctx->current_shader, name, value);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_shader->program,
name);
assert (location != -1);
dispatch->Uniform1f (location, value);
}
void
@ -1062,7 +884,11 @@ _cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
float value0,
float value1)
{
ctx->shader_impl->bind_vec2 (ctx, ctx->current_shader, name, value0, value1);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_shader->program,
name);
assert (location != -1);
dispatch->Uniform2f (location, value0, value1);
}
void
@ -1072,7 +898,11 @@ _cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
float value1,
float value2)
{
ctx->shader_impl->bind_vec3 (ctx, ctx->current_shader, name, value0, value1, value2);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_shader->program,
name);
assert (location != -1);
dispatch->Uniform3f (location, value0, value1, value2);
}
void
@ -1081,21 +911,38 @@ _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
float value0, float value1,
float value2, float value3)
{
ctx->shader_impl->bind_vec4 (ctx, ctx->current_shader, name, value0, value1, value2, value3);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_shader->program,
name);
assert (location != -1);
dispatch->Uniform4f (location, 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, ctx->current_shader, name, m);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_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);
dispatch->UniformMatrix3fv (location, 1, GL_TRUE, gl_m);
}
void
_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
const char *name, GLfloat* gl_m)
{
ctx->shader_impl->bind_matrix4f (ctx, ctx->current_shader, name, gl_m);
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location = dispatch->GetUniformLocation (ctx->current_shader->program,
name);
assert (location != -1);
dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
}
void
@ -1105,7 +952,10 @@ _cairo_gl_set_shader (cairo_gl_context_t *ctx,
if (ctx->current_shader == shader)
return;
ctx->shader_impl->use (ctx, shader);
if (shader)
ctx->dispatch.UseProgram (shader->program);
else
ctx->dispatch.UseProgram (0);
ctx->current_shader = shader;
}
@ -1164,12 +1014,12 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
entry->ctx = ctx;
_cairo_gl_shader_init (&entry->shader);
status = _cairo_gl_shader_compile (ctx,
&entry->shader,
cairo_gl_operand_get_var_type (source->type),
cairo_gl_operand_get_var_type (mask->type),
use_coverage,
fs_source);
status = _cairo_gl_shader_compile_and_link (ctx,
&entry->shader,
cairo_gl_operand_get_var_type (source->type),
cairo_gl_operand_get_var_type (mask->type),
use_coverage,
fs_source);
free (fs_source);
if (unlikely (status)) {