/* * Copyright (C) 2018, 2022 Tomasz Kramkowski * SPDX-License-Identifier: MIT */ #include #include #include #include #include #include "eprintf.h" #include "gl.h" #include "glprog.h" #include "nelem.h" enum { LOGSIZE = 1024, }; struct glprog_progs prog; static const struct { GLuint type; const char *name; bool reqd; const char *pre; } langs[] = { { GL_VERTEX_SHADER, "vert", true, #define SH_IN(type, name) "in " #type " " #name ";\n" #include "shaders/data.h" "out iface {\n" #define SH_IF(type, name) "\t" #type " " #name ";\n" #include "shaders/data.h" "} o;\n" }, { GL_TESS_CONTROL_SHADER, "tesc", false, "" }, { GL_TESS_EVALUATION_SHADER, "tese", false, "" }, { GL_GEOMETRY_SHADER, "geom", false, "" }, { GL_FRAGMENT_SHADER, "frag", true, #define SH_TEX(type, name) "uniform sampler" #type " t" #name ";\n" #define SH_OUT(type, name) "out " #type " " #name ";\n" #include "shaders/data.h" "in iface {\n" #define SH_IF(type, name) "\t" #type " " #name ";\n" #include "shaders/data.h" "} i;\n" }, }; // readfile: read a whole file into a buffer static bool readfile(char **data, size_t *size, const char *name) { FILE *f; size_t len = 0; assert(data != NULL); assert(size != NULL); assert(name != NULL); f = fopen(name, "rb"); if (f == NULL) return false; if (*size < 1024) { *size = 1024; *data = erealloc(*data, *size); } while (true) { size_t read = fread(*data + len, 1, *size - len, f); len += read; if (*size - len > 0) break; *size *= 2; *data = erealloc(*data, *size); } fclose(f); *size = len; *data = erealloc(*data, *size); return true; } // compile: compile shader component static GLuint compile(const char *src, size_t len, const char *pre, GLuint type) { GLuint id; GLint success; static const char *common_pre = #define SH_VER(v) "#version " v "\n" #define SH_UNI(type, name) "uniform " #type " " #name ";\n" #include "shaders/data.h" ; const char *srcs[] = { common_pre, pre, src }; GLint lens[] = { -1, -1, len }; id = gl_shdr_create(type); gl_shdr_source(id, NELEM(srcs), srcs, lens); gl_shdr_compile(id); gl_shdr_param(id, GL_COMPILE_STATUS, &success); if (!success) { char log[LOGSIZE]; gl_shdr_infolog(id, sizeof log, NULL, log); eprintf("Failed to compile shader:\n%s", log); } return id; } static void detach_shaders(GLuint prog) { GLsizei count; GLuint shdr; while (gl_prog_getshdrs(prog, 1, &count, &shdr), count) gl_prog_detachshdr(prog, shdr); } static void load_shader(struct shader *s, const char *path) { GLuint prog; GLint success, pos = 0; char *full, *src = NULL; size_t srclen = 0, fullsz; GLint texture = 0; assert(s != NULL); assert(path != NULL); fullsz = strlen(path) + strlen("/xxxx.glsl") + 1; assert(fullsz > strlen(path)); full = emalloc(fullsz); prog = gl_prog_create(); for (size_t i = 0; i < NELEM(langs); i++) { GLuint shdr; snprintf(full, fullsz, "%s/%s.glsl", path, langs[i].name); if (!readfile(&src, &srclen, full)) { if (!langs[i].reqd) continue; eprintf("Could not load shader file %s:\n", full); } shdr = compile(src, srclen, langs[i].pre, langs[i].type); gl_prog_attachshdr(prog, shdr); gl_shdr_del(shdr); } free(src); free(full); #define SH_IN(_, name) gl_attr_bindloc(prog, pos++, #name); #include "shaders/data.h" gl_prog_link(prog); gl_prog_param(prog, GL_LINK_STATUS, &success); if (!success) { char log[LOGSIZE]; gl_prog_infolog(prog, sizeof log, NULL, log); eprintf("Failed to link program\n%s", log); } detach_shaders(prog); gl_prog_use(prog); #define SH_UNI(_, name) s->uni.name = gl_uni_loc(prog, #name); #define SH_TEX(_, name) gl_uni_set1i(gl_uni_loc(prog, "t" #name), texture++); #include "shaders/data.h" s->prog = prog; } // glprog_init: compile and link all shader programs void glprog_init(void) { GLint pos = 0; #define SH_PROG(name) load_shader(&prog.name, "shaders/" #name); #define SH_IN(_, name) prog.attr.name = pos++; #include "shaders/data.h" }