summaryrefslogtreecommitdiff
path: root/src/shader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader.c')
-rw-r--r--src/shader.c253
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);
+}
© All Rights Reserved