diff options
Diffstat (limited to 'src/shader.c')
-rw-r--r-- | src/shader.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/shader.c b/src/shader.c new file mode 100644 index 0000000..bf9d325 --- /dev/null +++ b/src/shader.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2018-2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "glad.h" +#include "macros.h" + + +typedef struct shader_t { + unsigned program, refcnt; + unsigned n_uniforms, n_attributes; + int *uniforms, *attributes; + const char *vs_path, *fs_path; + struct timespec vs_mtime, fs_mtime; + + int locations[]; +} shader_t; + + +unsigned int shader_pair_new_bare(const char *vs_src, const char *fs_src) +{ + unsigned int vs, fs, shader; + int shader_success; + char shader_info[4096]; + + vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vs_src, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &shader_success); + if (!shader_success) { + glGetShaderInfoLog(vs, sizeof(shader_info), NULL, shader_info); + warn_if(1, "Error compiling vertex shader: \"%s\"", shader_info); + glDeleteShader(vs); + return 0; + } + + fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fs_src, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &shader_success); + if (!shader_success) { + glGetShaderInfoLog(fs, sizeof(shader_info), NULL, shader_info); + warn_if(1, "Error compiling fragment shader: \"%s\"", shader_info); + glDeleteShader(fs); + return 0; + } + + shader = glCreateProgram(); + glAttachShader(shader, vs); + glAttachShader(shader, fs); + glLinkProgram(shader); + glGetProgramiv(shader, GL_LINK_STATUS, &shader_success); + if (!shader_success) { + glGetProgramInfoLog(shader, sizeof(shader_info), NULL, shader_info); + warn_if(1, "Error linking shader program: \"%s\"", shader_info); + glDeleteProgram(shader); + shader = 0; + } + + glDeleteShader(vs); + glDeleteShader(fs); + + return shader; +} + + +shader_t * shader_pair_new(const char *vs_src, const char *fs_src, unsigned n_uniforms, const char **uniforms, unsigned n_attributes, const char **attributes) +{ + shader_t *shader; + + assert(vs_src); + assert(fs_src); + assert(uniforms || !n_uniforms); + assert(attributes || !n_attributes); + + shader = calloc(1, sizeof(shader_t) + (n_uniforms + n_attributes) * sizeof(int)); + fatal_if(!shader, "Unable to allocate shader"); + + shader->program = shader_pair_new_bare(vs_src, fs_src); + shader->refcnt++; + shader->n_uniforms = n_uniforms; + shader->n_attributes = n_attributes; + shader->uniforms = shader->locations; + shader->attributes = &shader->locations[n_uniforms]; + + for (unsigned i = 0; i < n_uniforms; i++) + shader->uniforms[i] = glGetUniformLocation(shader->program, uniforms[i]); + + for (unsigned i = 0; i < n_attributes; i++) + shader->attributes[i] = glGetAttribLocation(shader->program, attributes[i]); + + return shader; +} + + +static char * load_file(const char *path) +{ + char *buf = NULL; + FILE *f; + size_t size = 0, used = 0; + + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Unable to open \"%s\"", path); + return NULL; + } + + do { + size += 8192; + + buf = realloc(buf, size); + fatal_if(!buf, "unable to enlarge buf"); + + used += fread(&buf[used], 1, 8192, f); + } while (used == size); + + buf[used] = '\0'; + + fclose(f); + + return buf; +} + + +/* replace the shader program if the paths have been modified */ +/* returns -1 on failure + */ +int shader_reload_files(shader_t *shader) +{ + char *vs_src = NULL, *fs_src = NULL; + int ret = -1; + + assert(shader); + assert(shader->vs_path); + assert(shader->fs_path); + + vs_src = load_file(shader->vs_path); + fs_src = load_file(shader->fs_path); + + if (vs_src && fs_src) { + unsigned program; + + program = shader_pair_new_bare(vs_src, fs_src); + if (program) { + glDeleteProgram(shader->program); + + shader->program = program; + + ret = 1; + } + } + + free(vs_src); + free(fs_src); + + return ret; +} + + +shader_t * shader_pair_new_files(const char *vs_path, const char *fs_path, unsigned n_uniforms, const char **uniforms, unsigned n_attributes, const char **attributes) +{ + shader_t *shader; + + assert(vs_path); + assert(fs_path); + assert(uniforms || !n_uniforms); + assert(attributes || !n_attributes); + + shader = calloc(1, sizeof(shader_t) + (n_uniforms + n_attributes) * sizeof(int)); + fatal_if(!shader, "Unable to allocate shader"); + + shader->vs_path = strdup(vs_path); + fatal_if(!shader->vs_path, "unable to dup vs_path \"%s\"", vs_path); + + shader->fs_path = strdup(fs_path); + fatal_if(!shader->fs_path, "unable to dup fs_path \"%s\"", fs_path); + + (void) shader_reload_files(shader); + + shader->refcnt++; + shader->n_uniforms = n_uniforms; + shader->n_attributes = n_attributes; + shader->uniforms = shader->locations; + shader->attributes = &shader->locations[n_uniforms]; + + for (unsigned i = 0; i < n_uniforms; i++) + shader->uniforms[i] = glGetUniformLocation(shader->program, uniforms[i]); + + for (unsigned i = 0; i < n_attributes; i++) + shader->attributes[i] = glGetAttribLocation(shader->program, attributes[i]); + + return shader; +} + + +void shader_ref(shader_t *shader) +{ + assert(shader); + + shader->refcnt++; +} + + +shader_t * shader_free(shader_t *shader) +{ + assert(shader); + + shader->refcnt--; + if (shader->refcnt > 0) + return shader; + + glDeleteProgram(shader->program); + free(shader); + + return NULL; +} + + +void shader_use(shader_t *shader, unsigned *res_n_uniforms, int **res_uniforms, unsigned *res_n_attributes, int **res_attributes) +{ + assert(shader); + + if (res_n_uniforms) + *res_n_uniforms = shader->n_uniforms; + + if (res_uniforms) + *res_uniforms = shader->uniforms; + + if (res_n_attributes) + *res_n_attributes = shader->n_attributes; + + if (res_attributes) + *res_attributes = shader->attributes; + + glUseProgram(shader->program); +} |