/* * Copyright (C) 2018-2020 - Vito Caputo - * * 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 . */ #include #include #include #include #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); }