faros initial commit
[faros-demo] / src / sdr.c
1 #include <GL/glew.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <stdarg.h>
8 #include <assert.h>
9
10 #if defined(unix) || defined(__unix__)
11 #include <unistd.h>
12 #include <sys/stat.h>
13 #endif  /* unix */
14
15 #include "sdr.h"
16
17 static const char *sdrtypestr(unsigned int sdrtype);
18 static int sdrtypeidx(unsigned int sdrtype);
19
20
21 unsigned int create_vertex_shader(const char *src)
22 {
23         return create_shader(src, GL_VERTEX_SHADER);
24 }
25
26 unsigned int create_pixel_shader(const char *src)
27 {
28         return create_shader(src, GL_FRAGMENT_SHADER);
29 }
30
31 unsigned int create_tessctl_shader(const char *src)
32 {
33 #ifdef GL_TESS_CONTROL_SHADER
34         return create_shader(src, GL_TESS_CONTROL_SHADER);
35 #else
36         return 0;
37 #endif
38 }
39
40 unsigned int create_tesseval_shader(const char *src)
41 {
42 #ifdef GL_TESS_EVALUATION_SHADER
43         return create_shader(src, GL_TESS_EVALUATION_SHADER);
44 #else
45         return 0;
46 #endif
47 }
48
49 unsigned int create_geometry_shader(const char *src)
50 {
51 #ifdef GL_GEOMETRY_SHADER
52         return create_shader(src, GL_GEOMETRY_SHADER);
53 #else
54         return 0;
55 #endif
56 }
57
58 unsigned int create_shader(const char *src, unsigned int sdr_type)
59 {
60         unsigned int sdr;
61         int success, info_len;
62         char *info_str = 0;
63         const char *src_str[3], *header, *footer;
64         int src_str_count = 0;
65         GLenum err;
66
67         if((header = get_shader_header(sdr_type))) {
68                 src_str[src_str_count++] = header;
69         }
70         src_str[src_str_count++] = src;
71         if((footer = get_shader_footer(sdr_type))) {
72                 src_str[src_str_count++] = footer;
73         }
74
75         sdr = glCreateShader(sdr_type);
76         assert(glGetError() == GL_NO_ERROR);
77         glShaderSource(sdr, src_str_count, src_str, 0);
78         err = glGetError();
79         assert(err == GL_NO_ERROR);
80         glCompileShader(sdr);
81         assert(glGetError() == GL_NO_ERROR);
82
83         glGetShaderiv(sdr, GL_COMPILE_STATUS, &success);
84         assert(glGetError() == GL_NO_ERROR);
85         glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len);
86         assert(glGetError() == GL_NO_ERROR);
87
88         if(info_len) {
89                 if((info_str = malloc(info_len + 1))) {
90                         glGetShaderInfoLog(sdr, info_len, 0, info_str);
91                         assert(glGetError() == GL_NO_ERROR);
92                         info_str[info_len] = 0;
93                 }
94         }
95
96         if(success) {
97                 fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str);
98         } else {
99                 fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str);
100                 glDeleteShader(sdr);
101                 sdr = 0;
102         }
103
104         free(info_str);
105         return sdr;
106 }
107
108 void free_shader(unsigned int sdr)
109 {
110         glDeleteShader(sdr);
111 }
112
113 unsigned int load_vertex_shader(const char *fname)
114 {
115         return load_shader(fname, GL_VERTEX_SHADER);
116 }
117
118 unsigned int load_pixel_shader(const char *fname)
119 {
120         return load_shader(fname, GL_FRAGMENT_SHADER);
121 }
122
123 unsigned int load_tessctl_shader(const char *fname)
124 {
125 #ifdef GL_TESS_CONTROL_SHADER
126         return load_shader(fname, GL_TESS_CONTROL_SHADER);
127 #else
128         return 0;
129 #endif
130 }
131
132 unsigned int load_tesseval_shader(const char *fname)
133 {
134 #ifdef GL_TESS_EVALUATION_SHADER
135         return load_shader(fname, GL_TESS_EVALUATION_SHADER);
136 #else
137         return 0;
138 #endif
139 }
140
141 unsigned int load_geometry_shader(const char *fname)
142 {
143 #ifdef GL_GEOMETRY_SHADER
144         return load_shader(fname, GL_GEOMETRY_SHADER);
145 #else
146         return 0;
147 #endif
148 }
149
150 unsigned int load_shader(const char *fname, unsigned int sdr_type)
151 {
152         unsigned int sdr;
153         size_t filesize;
154         FILE *fp;
155         char *src;
156
157         if(!(fp = fopen(fname, "rb"))) {
158                 fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno));
159                 return 0;
160         }
161
162         fseek(fp, 0, SEEK_END);
163         filesize = ftell(fp);
164         fseek(fp, 0, SEEK_SET);
165
166         if(!(src = malloc(filesize + 1))) {
167                 fclose(fp);
168                 return 0;
169         }
170         fread(src, 1, filesize, fp);
171         src[filesize] = 0;
172         fclose(fp);
173
174         fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname);
175         sdr = create_shader(src, sdr_type);
176
177         free(src);
178         return sdr;
179 }
180
181
182 /* ---- gpu programs ---- */
183
184 unsigned int create_program(void)
185 {
186         unsigned int prog = glCreateProgram();
187         assert(glGetError() == GL_NO_ERROR);
188         return prog;
189 }
190
191 unsigned int create_program_link(unsigned int sdr0, ...)
192 {
193         unsigned int prog, sdr;
194         va_list ap;
195
196         if(!(prog = create_program())) {
197                 return 0;
198         }
199
200         attach_shader(prog, sdr0);
201         if(glGetError()) {
202                 return 0;
203         }
204
205         va_start(ap, sdr0);
206         while((sdr = va_arg(ap, unsigned int))) {
207                 attach_shader(prog, sdr);
208                 if(glGetError()) {
209                         return 0;
210                 }
211         }
212         va_end(ap);
213
214         if(link_program(prog) == -1) {
215                 free_program(prog);
216                 return 0;
217         }
218         return prog;
219 }
220
221 unsigned int create_program_load(const char *vfile, const char *pfile)
222 {
223         unsigned int vs = 0, ps = 0;
224
225         if(vfile && *vfile && !(vs = load_vertex_shader(vfile))) {
226                 return 0;
227         }
228         if(pfile && *pfile && !(ps = load_pixel_shader(pfile))) {
229                 return 0;
230         }
231         return create_program_link(vs, ps, 0);
232 }
233
234 void free_program(unsigned int sdr)
235 {
236         glDeleteProgram(sdr);
237 }
238
239 void attach_shader(unsigned int prog, unsigned int sdr)
240 {
241         int err;
242
243         if(prog && sdr) {
244                 assert(glGetError() == GL_NO_ERROR);
245                 glAttachShader(prog, sdr);
246                 if((err = glGetError()) != GL_NO_ERROR) {
247                         fprintf(stderr, "failed to attach shader %u to program %u (err: 0x%x)\n", sdr, prog, err);
248                         abort();
249                 }
250         }
251 }
252
253 int link_program(unsigned int prog)
254 {
255         int linked, info_len, retval = 0;
256         char *info_str = 0;
257
258         glLinkProgram(prog);
259         assert(glGetError() == GL_NO_ERROR);
260         glGetProgramiv(prog, GL_LINK_STATUS, &linked);
261         assert(glGetError() == GL_NO_ERROR);
262         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len);
263         assert(glGetError() == GL_NO_ERROR);
264
265         if(info_len) {
266                 if((info_str = malloc(info_len + 1))) {
267                         glGetProgramInfoLog(prog, info_len, 0, info_str);
268                         assert(glGetError() == GL_NO_ERROR);
269                         info_str[info_len] = 0;
270                 }
271         }
272
273         if(linked) {
274                 fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str);
275         } else {
276                 fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str);
277                 retval = -1;
278         }
279
280         free(info_str);
281         return retval;
282 }
283
284 int bind_program(unsigned int prog)
285 {
286         GLenum err;
287
288         glUseProgram(prog);
289         if(prog && (err = glGetError()) != GL_NO_ERROR) {
290                 /* maybe the program is not linked, try linking first */
291                 if(err == GL_INVALID_OPERATION) {
292                         if(link_program(prog) == -1) {
293                                 return -1;
294                         }
295                         glUseProgram(prog);
296                         return glGetError() == GL_NO_ERROR ? 0 : -1;
297                 }
298                 return -1;
299         }
300         return 0;
301 }
302
303 /* ugly but I'm not going to write the same bloody code over and over */
304 #define BEGIN_UNIFORM_CODE \
305         int loc, curr_prog; \
306         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \
307         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \
308                 return -1; \
309         } \
310         if((loc = glGetUniformLocation(prog, name)) != -1)
311
312 #define END_UNIFORM_CODE \
313         if((unsigned int)curr_prog != prog) { \
314                 bind_program(curr_prog); \
315         } \
316         return loc == -1 ? -1 : 0
317
318 int get_uniform_loc(unsigned int prog, const char *name)
319 {
320         int loc, curr_prog;
321         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog);
322         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) {
323                 return -1;
324         }
325         loc = glGetUniformLocation(prog, name);
326         if((unsigned int)curr_prog != prog) {
327                 bind_program(curr_prog);
328         }
329         return loc;
330 }
331
332 int set_uniform_int(unsigned int prog, const char *name, int val)
333 {
334         BEGIN_UNIFORM_CODE {
335                 glUniform1i(loc, val);
336         }
337         END_UNIFORM_CODE;
338 }
339
340 int set_uniform_float(unsigned int prog, const char *name, float val)
341 {
342         BEGIN_UNIFORM_CODE {
343                 glUniform1f(loc, val);
344         }
345         END_UNIFORM_CODE;
346 }
347
348 int set_uniform_float2(unsigned int prog, const char *name, float x, float y)
349 {
350         BEGIN_UNIFORM_CODE {
351                 glUniform2f(loc, x, y);
352         }
353         END_UNIFORM_CODE;
354 }
355
356 int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z)
357 {
358         BEGIN_UNIFORM_CODE {
359                 glUniform3f(loc, x, y, z);
360         }
361         END_UNIFORM_CODE;
362 }
363
364 int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w)
365 {
366         BEGIN_UNIFORM_CODE {
367                 glUniform4f(loc, x, y, z, w);
368         }
369         END_UNIFORM_CODE;
370 }
371
372 int set_uniform_matrix4(unsigned int prog, const char *name, const float *mat)
373 {
374         BEGIN_UNIFORM_CODE {
375                 glUniformMatrix4fv(loc, 1, GL_FALSE, mat);
376         }
377         END_UNIFORM_CODE;
378 }
379
380 int set_uniform_matrix4_transposed(unsigned int prog, const char *name, const float *mat)
381 {
382         BEGIN_UNIFORM_CODE {
383                 glUniformMatrix4fv(loc, 1, GL_TRUE, mat);
384         }
385         END_UNIFORM_CODE;
386 }
387
388 int get_attrib_loc(unsigned int prog, const char *name)
389 {
390         int loc, curr_prog;
391
392         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog);
393         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) {
394                 return -1;
395         }
396
397         loc = glGetAttribLocation(prog, (char*)name);
398
399         if((unsigned int)curr_prog != prog) {
400                 bind_program(curr_prog);
401         }
402         return loc;
403 }
404
405 void set_attrib_float3(int attr_loc, float x, float y, float z)
406 {
407         glVertexAttrib3f(attr_loc, x, y, z);
408 }
409
410 /* ---- shader composition ---- */
411 struct string {
412         char *text;
413         int len;
414 };
415
416 #define NUM_SHADER_TYPES        5
417 static struct string header[NUM_SHADER_TYPES];
418 static struct string footer[NUM_SHADER_TYPES];
419
420 static void clear_string(struct string *str)
421 {
422         free(str->text);
423         str->text = 0;
424         str->len = 0;
425 }
426
427 static void append_string(struct string *str, const char *s)
428 {
429         int len, newlen;
430         char *newstr;
431
432         if(!s || !*s) return;
433
434         len = strlen(s);
435         newlen = str->len + len;
436         if(!(newstr = malloc(newlen + 2))) {    /* leave space for a possible newline */
437                 fprintf(stderr, "shader composition: failed to append string of size %d\n", len);
438                 abort();
439         }
440
441         if(str->text) {
442                 memcpy(newstr, str->text, str->len);
443         }
444         memcpy(newstr + str->len, s, len + 1);
445
446         if(s[len - 1] != '\n') {
447                 newstr[newlen] = '\n';
448                 newstr[newlen + 1] = 0;
449         }
450
451         free(str->text);
452         str->text = newstr;
453         str->len = newlen;
454 }
455
456 void clear_shader_header(unsigned int type)
457 {
458         if(type) {
459                 int idx = sdrtypeidx(type);
460                 clear_string(&header[idx]);
461         } else {
462                 int i;
463                 for(i=0; i<NUM_SHADER_TYPES; i++) {
464                         clear_string(&header[i]);
465                 }
466         }
467 }
468
469 void clear_shader_footer(unsigned int type)
470 {
471         if(type) {
472                 int idx = sdrtypeidx(type);
473                 clear_string(&footer[idx]);
474         } else {
475                 int i;
476                 for(i=0; i<NUM_SHADER_TYPES; i++) {
477                         clear_string(&footer[i]);
478                 }
479         }
480 }
481
482 void add_shader_header(unsigned int type, const char *s)
483 {
484         if(type) {
485                 int idx = sdrtypeidx(type);
486                 append_string(&header[idx], s);
487         } else {
488                 int i;
489                 for(i=0; i<NUM_SHADER_TYPES; i++) {
490                         append_string(&header[i], s);
491                 }
492         }
493 }
494
495 void add_shader_footer(unsigned int type, const char *s)
496 {
497         if(type) {
498                 int idx = sdrtypeidx(type);
499                 append_string(&footer[idx], s);
500         } else {
501                 int i;
502                 for(i=0; i<NUM_SHADER_TYPES; i++) {
503                         append_string(&footer[i], s);
504                 }
505         }
506 }
507
508 const char *get_shader_header(unsigned int type)
509 {
510         int idx = sdrtypeidx(type);
511         return header[idx].text;
512 }
513
514 const char *get_shader_footer(unsigned int type)
515 {
516         int idx = sdrtypeidx(type);
517         return footer[idx].text;
518 }
519
520 static const char *sdrtypestr(unsigned int sdrtype)
521 {
522         switch(sdrtype) {
523         case GL_VERTEX_SHADER:
524                 return "vertex";
525         case GL_FRAGMENT_SHADER:
526                 return "pixel";
527 #ifdef GL_TESS_CONTROL_SHADER
528         case GL_TESS_CONTROL_SHADER:
529                 return "tessellation control";
530 #endif
531 #ifdef GL_TESS_EVALUATION_SHADER
532         case GL_TESS_EVALUATION_SHADER:
533                 return "tessellation evaluation";
534 #endif
535 #ifdef GL_GEOMETRY_SHADER
536         case GL_GEOMETRY_SHADER:
537                 return "geometry";
538 #endif
539
540         default:
541                 break;
542         }
543         return "<unknown>";
544 }
545
546 static int sdrtypeidx(unsigned int sdrtype)
547 {
548         switch(sdrtype) {
549         case GL_VERTEX_SHADER:
550                 return 0;
551         case GL_FRAGMENT_SHADER:
552                 return 1;
553         case GL_TESS_CONTROL_SHADER:
554                 return 2;
555         case GL_TESS_EVALUATION_SHADER:
556                 return 3;
557         case GL_GEOMETRY_SHADER:
558                 return 4;
559         default:
560                 break;
561         }
562         return 0;
563 }