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