polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

shader.c (7296B)


      1 
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 #include <assert.h>
      5 
      6 #include "file.h"
      7 #include "util.h"
      8 #include "gl.h"
      9 #include "debug.h"
     10 #include "shader.h"
     11 #include "common.h"
     12 
     13 #define MAX_LINES 4096
     14 
     15 static int file_buf_count = 0;
     16 static char *file_buffers[MAX_SHADER_INCLUDES];
     17 static char *file_names[MAX_SHADER_INCLUDES];
     18 static int file_name_lens[MAX_SHADER_INCLUDES];
     19 static char *line_buff[MAX_LINES];
     20 static u32 line_lens[MAX_LINES];
     21 
     22 static char *cached_file_contents(const char *filename) {
     23     size_t len;
     24     for (int i = 0; i < file_buf_count; i++) {
     25         if (memcmp(file_names[i], filename, file_name_lens[i]) == 0) {
     26             return file_buffers[i];
     27         }
     28     }
     29 
     30     return file_contents(filename, &len);
     31 }
     32 
     33 static char *strsep(char **stringp, const char *delim) {
     34     if (*stringp == NULL) { return NULL; }
     35     char *token_start = *stringp;
     36     *stringp = strpbrk(token_start, delim);
     37     if (*stringp)
     38         (*stringp)++;
     39     return token_start;
     40 }
     41 
     42 static char **resolve_imports(char *contents, int *nlines, int level) {
     43     char *line;
     44     int nline = 0;
     45     char *resolved_contents;
     46     char fname_buf[32] = {0};
     47 
     48     while ((line = strsep(&contents, "\n"))) {
     49         nline++;
     50         int line_len = contents - line;
     51         int size = sizeof("#include");
     52         if (memcmp(line, "#include ", size) == 0) {
     53             char *filename = line + size;
     54             int file_name_len = line_len - size - 1;
     55             snprintf(fname_buf, 32, SHADER("%.*s"), file_name_len, filename);
     56             file_name_lens[file_buf_count] = file_name_len;
     57             file_names[file_buf_count] = filename;
     58 
     59             /* printf("got include %.*s at line %d level %d ind %d\n", */
     60             /*         file_name_len, filename, nline, level, file_buf_count); */
     61 
     62             // TODO (perf): cache file contents based on filename
     63             resolved_contents = cached_file_contents(fname_buf);
     64             file_buffers[file_buf_count] = resolved_contents;
     65             file_buf_count++;
     66             resolve_imports(resolved_contents, nlines, level + 1);
     67         }
     68         else {
     69             line_buff[*nlines] = line;
     70             line_lens[*nlines] = line_len;
     71             (*nlines)++;
     72             /* printf("%d %.*s", *nlines, line_len, line); */
     73         }
     74     }
     75 
     76     return line_buff;
     77 }
     78 
     79 
     80 struct shader *get_program_shader(struct gpu_program *program, GLenum type) {
     81     struct shader *shader;
     82     for (int i = 0; i < program->n_shaders; i++) {
     83         shader = &program->shaders[i];
     84         if (shader->type == type)
     85             return shader;
     86     }
     87 
     88     return NULL;
     89 }
     90 
     91 
     92 int make_shader(GLenum type, const char *filename, struct shader *shader) {
     93   size_t length;
     94   int nlines = 0;
     95   GLchar *source = (GLchar *)file_contents(filename, &length);
     96   if (!source)
     97       return 0;
     98 
     99   assert(file_buf_count == 0);
    100   char **lines = resolve_imports(source, &nlines, 0);
    101 
    102   GLint shader_ok;
    103 
    104   shader->n_includes = 0;
    105   shader->filename = filename;
    106   shader->type = type;
    107   shader->handle = glCreateShader(type);
    108 
    109   glShaderSource(shader->handle, nlines, (const char**)lines,
    110                  (const int*)line_lens);
    111 
    112   // shader dependency stuff
    113   for (int i = 0; i < file_buf_count; ++i) {
    114       assert(i < MAX_SHADER_INCLUDES);
    115       char *p = shader->includes[shader->n_includes];
    116       int name_len = file_name_lens[i];
    117       /* printf("%.*s name len %d\n", name_len, file_names[i], name_len); */
    118       snprintf(p, MAX_INCLUDE_FNAME_LEN, SHADER("%.*s"), name_len, file_names[i]);
    119       assert(name_len < MAX_INCLUDE_FNAME_LEN);
    120       shader->include_mtimes[shader->n_includes] = file_mtime(p);
    121       /* printf("'%s' included in '%s'\n", p, shader->filename); */
    122       shader->n_includes++;
    123       free(file_buffers[i]);
    124   }
    125   free(source);
    126 
    127   file_buf_count = 0;
    128 
    129   glCompileShader(shader->handle);
    130 
    131   glGetShaderiv(shader->handle, GL_COMPILE_STATUS, &shader_ok);
    132 
    133   if (!shader_ok) {
    134     fprintf(stderr, "Failed to compile %s:\n", filename);
    135     show_info_log(shader->handle);
    136     glDeleteShader(shader->handle);
    137     return 0;
    138   }
    139 
    140   shader->load_mtime =
    141 	file_mtime(shader->filename);
    142 
    143   return 1;
    144 }
    145 
    146 #define N_SHADERS 3
    147 
    148 void init_gpu_program(struct gpu_program *program) {
    149     program->n_shaders = 0;
    150     memset(program->shaders, 0, sizeof(program->shaders));
    151 }
    152 
    153 #ifdef DEBUG
    154 int reload_program(struct gpu_program *program) {
    155 	int ok;
    156 
    157     int n_shaders = program->n_shaders;
    158     struct gpu_program new_program;
    159     struct shader new_shaders[n_shaders];
    160     struct shader *new_shaders_p[n_shaders];
    161 
    162     init_gpu_program(&new_program);
    163 
    164     int changes[n_shaders];
    165 
    166     for (int i = 0; i < n_shaders; i++) {
    167         changes[i] = 0;
    168         struct shader *shader = &program->shaders[i];
    169         new_shaders_p[i] = shader;
    170 
    171         time_t mtime = file_mtime(shader->filename);
    172         int changed = mtime != shader->load_mtime;
    173 
    174         for (int j = 0; j < shader->n_includes; ++j) {
    175             time_t include_mtime = shader->include_mtimes[j];
    176             time_t new_mtime = file_mtime(shader->includes[j]);
    177             changed |= include_mtime != new_mtime;
    178         }
    179  
    180         changes[i] = changed;
    181 
    182         if (changed) {
    183             /* printf("making shader %s\n", shader->filename); */
    184             ok = make_shader(shader->type, shader->filename, &new_shaders[i]);
    185             if (!ok) {
    186                 // clean up all the new shaders we've created so far
    187                 for (int k = 0; k < i; k++)
    188                     glDeleteShader(new_shaders[k].handle);
    189                 return 0;
    190             }
    191             new_shaders_p[i] = &new_shaders[i];
    192         }
    193     }
    194 
    195     int any_changes = 0;
    196     for (int i = 0; i < n_shaders; i++)
    197         any_changes |= changes[i];
    198 
    199     if (!any_changes) {
    200         return 2;
    201     }
    202 
    203 	ok = make_program_from_shaders(new_shaders_p, n_shaders, &new_program);
    204 
    205     if (!ok) {
    206         for (int i = 0; i < n_shaders; i++) {
    207             glDeleteShader(program->shaders[i].handle);
    208         }
    209         return 0;
    210     }
    211 
    212     *program = new_program;
    213     return 1;
    214 }
    215 #endif
    216 
    217 int
    218 make_program(struct shader *vertex,
    219              struct shader *fragment,
    220 	         struct gpu_program *program)
    221 {
    222     struct shader *shaders[] = { vertex, fragment };
    223     return make_program_from_shaders(shaders, 2, program);
    224 }
    225 
    226 int
    227 make_program_from_shaders(struct shader **shaders, int n_shaders, struct gpu_program *program)
    228 {
    229 	GLint program_ok;
    230 
    231 	// TODO: relax these constraints
    232 	program->handle = glCreateProgram();
    233     check_gl();
    234     program->n_shaders = n_shaders;
    235 
    236     assert(n_shaders <= MAX_SHADERS);
    237     for (int i = 0; i < n_shaders; i++) {
    238         struct shader *shader = shaders[i];
    239         program->shaders[i] = *shader;
    240         /* printf("attaching shader %s\n", shader->filename); */
    241         /* for (int j = 0; j < shader->n_includes; ++j) { */
    242         /*     printf("attaching shader %s dep %s \n", shader->filename, */
    243         /*            shader->includes[j]); */
    244         /* } */
    245         glAttachShader(program->handle, shader->handle);
    246         check_gl();
    247     }
    248 
    249 	glLinkProgram(program->handle);
    250     check_gl();
    251 
    252 	glGetProgramiv(program->handle, GL_LINK_STATUS, &program_ok);
    253 
    254 	if (!program_ok) {
    255 		fprintf(stderr, "Failed to link shader program:\n");
    256 		show_info_log(program->handle);
    257 		glDeleteProgram(program->handle);
    258 		return 0;
    259 	}
    260 
    261 	return 1;
    262 }
    263