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