5ccf7fe2dc9d28b77f54079a1584e7e74cc72e66
[vrfileman] / 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 static const char *sdrtypestr(unsigned int sdrtype);
17
18 unsigned int create_vertex_shader(const char *src)
19 {
20         return create_shader(src, GL_VERTEX_SHADER);
21 }
22
23 unsigned int create_pixel_shader(const char *src)
24 {
25         return create_shader(src, GL_FRAGMENT_SHADER);
26 }
27
28 unsigned int create_tessctl_shader(const char *src)
29 {
30 #ifdef GL_TESS_CONTROL_SHADER
31         return create_shader(src, GL_TESS_CONTROL_SHADER);
32 #else
33         return 0;
34 #endif
35 }
36
37 unsigned int create_tesseval_shader(const char *src)
38 {
39 #ifdef GL_TESS_EVALUATION_SHADER
40         return create_shader(src, GL_TESS_EVALUATION_SHADER);
41 #else
42         return 0;
43 #endif
44 }
45
46 unsigned int create_geometry_shader(const char *src)
47 {
48 #ifdef GL_GEOMETRY_SHADER
49         return create_shader(src, GL_GEOMETRY_SHADER);
50 #else
51         return 0;
52 #endif
53 }
54
55 unsigned int create_shader(const char *src, unsigned int sdr_type)
56 {
57         unsigned int sdr;
58         int success, info_len;
59         char *info_str = 0;
60         GLenum err;
61
62         sdr = glCreateShader(sdr_type);
63         assert(glGetError() == GL_NO_ERROR);
64         glShaderSource(sdr, 1, &src, 0);
65         err = glGetError();
66         assert(err == GL_NO_ERROR);
67         glCompileShader(sdr);
68         assert(glGetError() == GL_NO_ERROR);
69
70         glGetShaderiv(sdr, GL_COMPILE_STATUS, &success);
71         assert(glGetError() == GL_NO_ERROR);
72         glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len);
73         assert(glGetError() == GL_NO_ERROR);
74
75         if(info_len) {
76                 if((info_str = malloc(info_len + 1))) {
77                         glGetShaderInfoLog(sdr, info_len, 0, info_str);
78                         assert(glGetError() == GL_NO_ERROR);
79                         info_str[info_len] = 0;
80                 }
81         }
82
83         if(success) {
84                 fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str);
85         } else {
86                 fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str);
87                 glDeleteShader(sdr);
88                 sdr = 0;
89         }
90
91         free(info_str);
92         return sdr;
93 }
94
95 void free_shader(unsigned int sdr)
96 {
97         glDeleteShader(sdr);
98 }
99
100 unsigned int load_vertex_shader(const char *fname)
101 {
102         return load_shader(fname, GL_VERTEX_SHADER);
103 }
104
105 unsigned int load_pixel_shader(const char *fname)
106 {
107         return load_shader(fname, GL_FRAGMENT_SHADER);
108 }
109
110 unsigned int load_tessctl_shader(const char *fname)
111 {
112 #ifdef GL_TESS_CONTROL_SHADER
113         return load_shader(fname, GL_TESS_CONTROL_SHADER);
114 #else
115         return 0;
116 #endif
117 }
118
119 unsigned int load_tesseval_shader(const char *fname)
120 {
121 #ifdef GL_TESS_EVALUATION_SHADER
122         return load_shader(fname, GL_TESS_EVALUATION_SHADER);
123 #else
124         return 0;
125 #endif
126 }
127
128 unsigned int load_geometry_shader(const char *fname)
129 {
130 #ifdef GL_GEOMETRY_SHADER
131         return load_shader(fname, GL_GEOMETRY_SHADER);
132 #else
133         return 0;
134 #endif
135 }
136
137 unsigned int load_shader(const char *fname, unsigned int sdr_type)
138 {
139         unsigned int sdr;
140         size_t filesize;
141         FILE *fp;
142         char *src;
143
144         if(!(fp = fopen(fname, "rb"))) {
145                 fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno));
146                 return 0;
147         }
148
149         fseek(fp, 0, SEEK_END);
150         filesize = ftell(fp);
151         fseek(fp, 0, SEEK_SET);
152
153         if(!(src = malloc(filesize + 1))) {
154                 fclose(fp);
155                 return 0;
156         }
157         fread(src, 1, filesize, fp);
158         src[filesize] = 0;
159         fclose(fp);
160
161         fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname);
162         sdr = create_shader(src, sdr_type);
163
164         free(src);
165         return sdr;
166 }
167
168
169 /* ---- gpu programs ---- */
170
171 unsigned int create_program(void)
172 {
173         unsigned int prog = glCreateProgram();
174         assert(glGetError() == GL_NO_ERROR);
175         return prog;
176 }
177
178 unsigned int create_program_link(unsigned int sdr0, ...)
179 {
180         unsigned int prog, sdr;
181         va_list ap;
182
183         if(!(prog = create_program())) {
184                 return 0;
185         }
186
187         attach_shader(prog, sdr0);
188         if(glGetError()) {
189                 return 0;
190         }
191
192         va_start(ap, sdr0);
193         while((sdr = va_arg(ap, unsigned int))) {
194                 attach_shader(prog, sdr);
195                 if(glGetError()) {
196                         return 0;
197                 }
198         }
199         va_end(ap);
200
201         if(link_program(prog) == -1) {
202                 free_program(prog);
203                 return 0;
204         }
205         return prog;
206 }
207
208 unsigned int create_program_load(const char *vfile, const char *pfile)
209 {
210         unsigned int vs = 0, ps = 0;
211
212         if(vfile && *vfile && !(vs = load_vertex_shader(vfile))) {
213                 return 0;
214         }
215         if(pfile && *pfile && !(ps = load_pixel_shader(pfile))) {
216                 return 0;
217         }
218         return create_program_link(vs, ps, 0);
219 }
220
221 void free_program(unsigned int sdr)
222 {
223         glDeleteProgram(sdr);
224 }
225
226 void attach_shader(unsigned int prog, unsigned int sdr)
227 {
228         int err;
229
230         if(prog && sdr) {
231                 assert(glGetError() == GL_NO_ERROR);
232                 glAttachShader(prog, sdr);
233                 if((err = glGetError()) != GL_NO_ERROR) {
234                         fprintf(stderr, "failed to attach shader %u to program %u (err: 0x%x)\n", sdr, prog, err);
235                         abort();
236                 }
237         }
238 }
239
240 int link_program(unsigned int prog)
241 {
242         int linked, info_len, retval = 0;
243         char *info_str = 0;
244
245         glLinkProgram(prog);
246         assert(glGetError() == GL_NO_ERROR);
247         glGetProgramiv(prog, GL_LINK_STATUS, &linked);
248         assert(glGetError() == GL_NO_ERROR);
249         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len);
250         assert(glGetError() == GL_NO_ERROR);
251
252         if(info_len) {
253                 if((info_str = malloc(info_len + 1))) {
254                         glGetProgramInfoLog(prog, info_len, 0, info_str);
255                         assert(glGetError() == GL_NO_ERROR);
256                         info_str[info_len] = 0;
257                 }
258         }
259
260         if(linked) {
261                 fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str);
262         } else {
263                 fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str);
264                 retval = -1;
265         }
266
267         free(info_str);
268         return retval;
269 }
270
271 int bind_program(unsigned int prog)
272 {
273         GLenum err;
274
275         glUseProgram(prog);
276         if(prog && (err = glGetError()) != GL_NO_ERROR) {
277                 /* maybe the program is not linked, try linking first */
278                 if(err == GL_INVALID_OPERATION) {
279                         if(link_program(prog) == -1) {
280                                 return -1;
281                         }
282                         glUseProgram(prog);
283                         return glGetError() == GL_NO_ERROR ? 0 : -1;
284                 }
285                 return -1;
286         }
287         return 0;
288 }
289
290 /* ugly but I'm not going to write the same bloody code over and over */
291 #define BEGIN_UNIFORM_CODE \
292         int loc, curr_prog; \
293         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \
294         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \
295                 return -1; \
296         } \
297         if((loc = glGetUniformLocation(prog, name)) != -1)
298
299 #define END_UNIFORM_CODE \
300         if((unsigned int)curr_prog != prog) { \
301                 bind_program(curr_prog); \
302         } \
303         return loc == -1 ? -1 : 0
304
305 int get_uniform_loc(unsigned int prog, const char *name)
306 {
307         int loc, curr_prog;
308         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog);
309         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) {
310                 return -1;
311         }
312         loc = glGetUniformLocation(prog, name);
313         if((unsigned int)curr_prog != prog) {
314                 bind_program(curr_prog);
315         }
316         return loc;
317 }
318
319 int set_uniform_int(unsigned int prog, const char *name, int val)
320 {
321         BEGIN_UNIFORM_CODE {
322                 glUniform1i(loc, val);
323         }
324         END_UNIFORM_CODE;
325 }
326
327 int set_uniform_float(unsigned int prog, const char *name, float val)
328 {
329         BEGIN_UNIFORM_CODE {
330                 glUniform1f(loc, val);
331         }
332         END_UNIFORM_CODE;
333 }
334
335 int set_uniform_float2(unsigned int prog, const char *name, float x, float y)
336 {
337         BEGIN_UNIFORM_CODE {
338                 glUniform2f(loc, x, y);
339         }
340         END_UNIFORM_CODE;
341 }
342
343 int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z)
344 {
345         BEGIN_UNIFORM_CODE {
346                 glUniform3f(loc, x, y, z);
347         }
348         END_UNIFORM_CODE;
349 }
350
351 int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w)
352 {
353         BEGIN_UNIFORM_CODE {
354                 glUniform4f(loc, x, y, z, w);
355         }
356         END_UNIFORM_CODE;
357 }
358
359 int set_uniform_matrix4(unsigned int prog, const char *name, float *mat)
360 {
361         BEGIN_UNIFORM_CODE {
362                 glUniformMatrix4fv(loc, 1, GL_FALSE, mat);
363         }
364         END_UNIFORM_CODE;
365 }
366
367 int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat)
368 {
369         BEGIN_UNIFORM_CODE {
370                 glUniformMatrix4fv(loc, 1, GL_TRUE, mat);
371         }
372         END_UNIFORM_CODE;
373 }
374
375 int get_attrib_loc(unsigned int prog, const char *name)
376 {
377         int loc, curr_prog;
378
379         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog);
380         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) {
381                 return -1;
382         }
383
384         loc = glGetAttribLocation(prog, (char*)name);
385
386         if((unsigned int)curr_prog != prog) {
387                 bind_program(curr_prog);
388         }
389         return loc;
390 }
391
392 void set_attrib_float3(int attr_loc, float x, float y, float z)
393 {
394         glVertexAttrib3f(attr_loc, x, y, z);
395 }
396
397 static const char *sdrtypestr(unsigned int sdrtype)
398 {
399         switch(sdrtype) {
400         case GL_VERTEX_SHADER:
401                 return "vertex";
402         case GL_FRAGMENT_SHADER:
403                 return "pixel";
404 #ifdef GL_TESS_CONTROL_SHADER
405         case GL_TESS_CONTROL_SHADER:
406                 return "tessellation control";
407 #endif
408 #ifdef GL_TESS_EVALUATION_SHADER
409         case GL_TESS_EVALUATION_SHADER:
410                 return "tessellation evaluation";
411 #endif
412 #ifdef GL_GEOMETRY_SHADER
413         case GL_GEOMETRY_SHADER:
414                 return "geometry";
415 #endif
416
417         default:
418                 break;
419         }
420         return "<unknown>";
421 }