diff options
author | David Herrmann <dh.herrmann@googlemail.com> | 2012-03-23 12:50:35 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@googlemail.com> | 2012-03-23 12:50:35 +0100 |
commit | 25f41f84f784f5a1aae612a5824bfa4aa84ecf40 (patch) | |
tree | ebc40ea6fe1cdd40e79d5b526625e6f8b0f64c38 /src | |
parent | 50e0cdbec805e4685847f1424950446d59a2412a (diff) |
gl: add GL subsystem
The GL subsystem is copied from the old output_context subsystem and now
provides shader and GL-math. It will replace the old output_context subs
soon.
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gl.h | 93 | ||||
-rw-r--r-- | src/gl_math.c | 304 | ||||
-rw-r--r-- | src/gl_shader.c | 365 |
3 files changed, 762 insertions, 0 deletions
diff --git a/src/gl.h b/src/gl.h new file mode 100644 index 0000000..5bf9694 --- /dev/null +++ b/src/gl.h @@ -0,0 +1,93 @@ +/* + * GL - Graphics Layer + * + * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Graphics Layer + * This provides lots of helpers to work with OpenGL APIs. This includes + * math-helpers, basic shaders and a texture-API. If working with this API, + * there must always be a valid OpenGL-context! + */ + +#ifndef GL_GL_H +#define GL_GL_H + +#include <stdbool.h> +#include <stdlib.h> + +/* miscellaneous */ +void gl_clear_error(); +bool gl_has_error(); + +/* + * Math Helpers + * The gl_m4 type is a 4x4 matrix of floats. The gl_m4_stack is a stack of m4 + * matrices where you can only access the top-most member. + */ + +struct gl_m4_stack; + +void gl_m4_identity(float *m); +void gl_m4_copy(float *dest, const float *src); +void gl_m4_mult_dest(float *dest, const float *n, const float *m); +void gl_m4_mult(float *n, const float *m); +void gl_m4_translate(float *m, float x, float y, float z); +void gl_m4_scale(float *m, float x, float y, float z); +void gl_m4_transpose_dest(float *dest, const float *src); +void gl_m4_transpose(float *m); + +int gl_m4_stack_new(struct gl_m4_stack **out); +void gl_m4_stack_free(struct gl_m4_stack *stack); +float *gl_m4_stack_push(struct gl_m4_stack *stack); +float *gl_m4_stack_pop(struct gl_m4_stack *stack); +float *gl_m4_stack_tip(struct gl_m4_stack *stack); + +/* + * Texture API + * This allows to create new textures which can then be used to draw images with + * the shader API. + */ + +unsigned int gl_tex_new(); +void gl_tex_free(unsigned int tex); +void gl_tex_load(unsigned int tex, unsigned int width, + unsigned int height, void *buf); + +/* + * Shader API + */ + +struct gl_shader; + +int gl_shader_new(struct gl_shader **out); +void gl_shader_ref(struct gl_shader *shader); +void gl_shader_unref(struct gl_shader *shader); + +void gl_shader_draw_def(struct gl_shader *shader, float *vertices, + float *colors, size_t num); +void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices, + const float *texcoords, size_t num, + unsigned int tex, const float *m); + +#endif /* GL_GL_H */ diff --git a/src/gl_math.c b/src/gl_math.c new file mode 100644 index 0000000..5cc40e8 --- /dev/null +++ b/src/gl_math.c @@ -0,0 +1,304 @@ +/* + * GL - Graphics Layer + * + * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com> + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Math Helper + * Basic linear algebra. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "gl.h" + +struct gl_m4_entry { + struct gl_m4_entry *next; + float matrix[16]; +}; + +struct gl_m4_stack { + struct gl_m4_entry stack; + struct gl_m4_entry *cache; +}; + +void gl_m4_identity(float *m) +{ + if (!m) + return; + + m[0] = 1; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = 1; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = 1; + m[11] = 0; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; +} + +void gl_m4_copy(float *dest, const float *src) +{ + if (!dest || !src) + return; + + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + + dest[4] = src[4]; + dest[5] = src[5]; + dest[6] = src[6]; + dest[7] = src[7]; + + dest[8] = src[8]; + dest[9] = src[9]; + dest[10] = src[10]; + dest[11] = src[11]; + + dest[12] = src[12]; + dest[13] = src[13]; + dest[14] = src[14]; + dest[15] = src[15]; +} + +/* Matrix Multiplication + * This is the simplest algorithm to multiply two matrices. It computes: + * dest = n * m + * That is, n is left side and m is right side. + * + * Matrix-multiplication is heavy, avoid it if possible. + */ +void gl_m4_mult_dest(float *dest, const float *n, const float *m) +{ + unsigned int row, col, j; + + if (!dest || !n || !m) + return; + + for (row = 0; row < 4; ++row) { + for (col = 0; col < 4; ++col) { + dest[row * 4 + col] = 0; + for (j = 0; j < 4; ++j) + dest[row * 4 + col] += + n[row * 4 + j] * m[j * 4 + col]; + } + } +} + +/* this computes n = n * m */ +void gl_m4_mult(float *n, const float *m) +{ + float tmp[16]; + + if (!n || !m) + return; + + gl_m4_mult_dest(tmp, n, m); + gl_m4_copy(n, tmp); +} + +void gl_m4_translate(float *m, float x, float y, float z) +{ + float trans[16] = { 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 }; + + if (!m) + return; + + gl_m4_mult(m, trans); +} + +void gl_m4_scale(float *m, float x, float y, float z) +{ + float scale[16] = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 }; + + if (!m) + return; + + gl_m4_mult(m, scale); +} + +void gl_m4_transpose_dest(float *dest, const float *src) +{ + if (!dest || !src) + return; + + dest[0] = src[0]; + dest[5] = src[5]; + dest[10] = src[10]; + dest[15] = src[15]; + + dest[1] = src[4]; + dest[4] = src[1]; + + dest[8] = src[2]; + dest[2] = src[8]; + + dest[3] = src[12]; + dest[12] = src[3]; + + dest[7] = src[13]; + dest[13] = src[7]; + + dest[11] = src[14]; + dest[14] = src[11]; + + dest[6] = src[9]; + dest[9] = src[6]; +} + +void gl_m4_transpose(float *m) +{ + float tmp; + + if (!m) + return; + + tmp = m[1]; + m[1] = m[4]; + m[4] = tmp; + + tmp = m[8]; + m[8] = m[2]; + m[2] = tmp; + + tmp = m[3]; + m[3] = m[12]; + m[12] = tmp; + + tmp = m[7]; + m[7] = m[13]; + m[13] = tmp; + + tmp = m[11]; + m[11] = m[14]; + m[14] = tmp; + + tmp = m[6]; + m[6] = m[9]; + m[9] = tmp; +} + +int gl_m4_stack_new(struct gl_m4_stack **out) +{ + struct gl_m4_stack *stack; + + if (!out) + return -EINVAL; + + stack = malloc(sizeof(*stack)); + if (!stack) + return -ENOMEM; + + memset(stack, 0, sizeof(*stack)); + gl_m4_identity(stack->stack.matrix); + + *out = stack; + return 0; +} + +void gl_m4_stack_free(struct gl_m4_stack *stack) +{ + struct gl_m4_entry *tmp; + + if (!stack) + return; + + while (stack->stack.next) { + tmp = stack->stack.next; + stack->stack.next = tmp->next; + free(tmp); + } + + while (stack->cache) { + tmp = stack->cache; + stack->cache = tmp->next; + free(tmp); + } + + free(stack); +} + +float *gl_m4_stack_push(struct gl_m4_stack *stack) +{ + struct gl_m4_entry *entry; + + if (stack->cache) { + entry = stack->cache; + stack->cache = entry->next; + } else { + entry = malloc(sizeof(*entry)); + if (!entry) + return NULL; + } + + gl_m4_copy(entry->matrix, stack->stack.matrix); + entry->next = stack->stack.next; + stack->stack.next = entry; + + return stack->stack.matrix; +} + +float *gl_m4_stack_pop(struct gl_m4_stack *stack) +{ + struct gl_m4_entry *entry; + + if (!stack) + return NULL; + + entry = stack->stack.next; + if (!entry) { + gl_m4_identity(stack->stack.matrix); + return stack->stack.matrix; + } + + stack->stack.next = entry->next; + entry->next = stack->cache; + stack->cache = entry; + + gl_m4_copy(stack->stack.matrix, entry->matrix); + + return stack->stack.matrix; +} + +float *gl_m4_stack_tip(struct gl_m4_stack *stack) +{ + if (!stack) + return NULL; + + return stack->stack.matrix; +} diff --git a/src/gl_shader.c b/src/gl_shader.c new file mode 100644 index 0000000..d10ccd4 --- /dev/null +++ b/src/gl_shader.c @@ -0,0 +1,365 @@ +/* + * GL - Graphics Layer + * + * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com> + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Shader API + * This provides basic shader objects that are used to draw sprites and + * textures. + */ + +#define GL_GLEXT_PROTOTYPES + +#include <errno.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "gl.h" +#include "output.h" +#include "log.h" + +#define LOG_SUBSYSTEM "gl" + +/* Clear the GL error stack. The standard says that the error value is just a + * single value and no list/stack. However, multiple error fields may be defined + * and glGetError() returns only one of them until all are cleared. Hence, we + * loop until no more error is retrieved. + */ +void gl_clear_error() +{ + GLenum err; + + do { + err = glGetError(); + } while (err != GL_NO_ERROR); +} + +/* return true if there is a pending GL error */ +bool gl_has_error() +{ + GLenum err; + + err = glGetError(); + if (err != GL_NO_ERROR) { + log_err("GL error %d", err); + return true; + } + + return false; +} + +unsigned int gl_tex_new() +{ + GLuint tex = 0; + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + return tex; +} + +void gl_tex_free(unsigned int tex) +{ + glDeleteTextures(1, &tex); +} + +void gl_tex_load(unsigned int tex, unsigned int width, + unsigned int height, void *buf) +{ + if (!buf || !width || !height) + return; + + /* With OpenGL instead of OpenGLES2 we must use this on linux: + * glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, + * GL_UNSIGNED_BYTE, buf); + */ + + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0, + GL_BGRA_EXT, GL_UNSIGNED_BYTE, buf); +} + +struct gl_shader { + unsigned long ref; + + GLuint def_program; + GLuint def_vshader; + GLuint def_fshader; + GLuint def_uni_projection; + + GLuint tex_program; + GLuint tex_vshader; + GLuint tex_fshader; + GLuint tex_uni_projection; + GLuint tex_uni_texture; +}; + +/* external shader sources; generated during build */ +extern const char *kmscon_vert_def; +extern const char *kmscon_frag_def; +extern const char *kmscon_vert_tex; +extern const char *kmscon_frag_tex; + +static int compile_shader(GLenum type, const char *source) +{ + char msg[512]; + GLint status = 1; + GLuint s; + + s = glCreateShader(type); + if (s == GL_NONE) { + log_warn("cannot allocate GL shader"); + return GL_NONE; + } + + glShaderSource(s, 1, &source, NULL); + glCompileShader(s); + + glGetShaderiv(s, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + msg[0] = 0; + glGetShaderInfoLog(s, sizeof(msg), NULL, msg); + log_warn("cannot compile shader: %s", msg); + return GL_NONE; + } + + return s; +} + +static int init_def_shader(struct gl_shader *shader) +{ + char msg[512]; + GLint status = 1; + int ret; + + shader->def_vshader = compile_shader(GL_VERTEX_SHADER, + kmscon_vert_def); + if (shader->def_vshader == GL_NONE) + return -EFAULT; + + shader->def_fshader = compile_shader(GL_FRAGMENT_SHADER, + kmscon_frag_def); + if (shader->def_fshader == GL_NONE) { + ret = -EFAULT; + goto err_vshader; + } + + shader->def_program = glCreateProgram(); + glAttachShader(shader->def_program, shader->def_vshader); + glAttachShader(shader->def_program, shader->def_fshader); + glBindAttribLocation(shader->def_program, 0, "position"); + glBindAttribLocation(shader->def_program, 1, "color"); + + glLinkProgram(shader->def_program); + glGetProgramiv(shader->def_program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + msg[0] = 0; + glGetProgramInfoLog(shader->def_program, sizeof(msg), + NULL, msg); + log_warn("cannot link shader: %s", msg); + ret = -EFAULT; + goto err_link; + } + + shader->def_uni_projection = + glGetUniformLocation(shader->def_program, "projection"); + + return 0; + +err_link: + glDeleteProgram(shader->def_program); + glDeleteShader(shader->def_fshader); +err_vshader: + glDeleteShader(shader->def_vshader); + return ret; +} + +static void free_def_shader(struct gl_shader *shader) +{ + glDeleteProgram(shader->def_program); + glDeleteShader(shader->def_fshader); + glDeleteShader(shader->def_vshader); +} + +static int init_tex_shader(struct gl_shader *shader) +{ + char msg[512]; + GLint status = 1; + int ret; + + shader->tex_vshader = compile_shader(GL_VERTEX_SHADER, + kmscon_vert_tex); + if (shader->tex_vshader == GL_NONE) + return -EFAULT; + + shader->tex_fshader = compile_shader(GL_FRAGMENT_SHADER, + kmscon_frag_tex); + if (shader->tex_fshader == GL_NONE) { + ret = -EFAULT; + goto err_vshader; + } + + shader->tex_program = glCreateProgram(); + glAttachShader(shader->tex_program, shader->tex_vshader); + glAttachShader(shader->tex_program, shader->tex_fshader); + glBindAttribLocation(shader->tex_program, 0, "position"); + glBindAttribLocation(shader->tex_program, 1, "texture_position"); + + glLinkProgram(shader->tex_program); + glGetProgramiv(shader->tex_program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + msg[0] = 0; + glGetProgramInfoLog(shader->tex_program, sizeof(msg), + NULL, msg); + log_warn("cannot link shader: %s", msg); + ret = -EFAULT; + goto err_link; + } + + shader->tex_uni_projection = + glGetUniformLocation(shader->tex_program, "projection"); + shader->tex_uni_texture = + glGetUniformLocation(shader->tex_program, "texture"); + + return 0; + +err_link: + glDeleteProgram(shader->tex_program); + glDeleteShader(shader->tex_fshader); +err_vshader: + glDeleteShader(shader->tex_vshader); + return ret; +} + +static void free_tex_shader(struct gl_shader *shader) +{ + glDeleteProgram(shader->tex_program); + glDeleteShader(shader->tex_fshader); + glDeleteShader(shader->tex_vshader); +} + +int gl_shader_new(struct gl_shader **out) +{ + struct gl_shader *shader; + int ret; + + if (!out) + return -EINVAL; + + shader = malloc(sizeof(*shader)); + if (!shader) + return -ENOMEM; + memset(shader, 0, sizeof(*shader)); + shader->ref = 1; + + ret = init_def_shader(shader); + if (ret) + goto err_free; + + ret = init_tex_shader(shader); + if (ret) + goto err_def; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + *out = shader; + return 0; + +err_def: + free_def_shader(shader); +err_free: + free(shader); + return ret; +} + +void gl_shader_ref(struct gl_shader *shader) +{ + if (!shader || !shader->ref) + return; + + ++shader->ref; +} + +void gl_shader_unref(struct gl_shader *shader) +{ + if (!shader || !shader->ref || --shader->ref) + return; + + free_tex_shader(shader); + free_def_shader(shader); + free(shader); +} + +void gl_shader_draw_def(struct gl_shader *shader, float *vertices, + float *colors, size_t num) +{ + float m[16] = { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + + if (!shader || !vertices || !colors || !num) + return; + + glUseProgram(shader->def_program); + glUniformMatrix4fv(shader->def_uni_projection, 1, GL_FALSE, m); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glDrawArrays(GL_TRIANGLES, 0, num); +} + +void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices, + const float *texcoords, size_t num, + unsigned int tex, const float *m) +{ + float mat[16]; + + if (!shader || !vertices || !texcoords || !num || !m) + return; + + gl_m4_transpose_dest(mat, m); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + + glUseProgram(shader->tex_program); + glUniformMatrix4fv(shader->tex_uni_projection, 1, GL_FALSE, mat); + glUniform1i(shader->tex_uni_texture, 0); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoords); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glDrawArrays(GL_TRIANGLES, 0, num); +} |