first render
[nexus3d] / src / gl / gfx_gl.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <alloca.h>
6 #include "opengl.h"
7 #include "nexus3d_impl.h"
8 #include "gfx_gl.h"
9
10 static unsigned int cur_state;
11
12 void nex_clear(void)
13 {
14         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
15 }
16
17 void nex_clearcolor(float r, float g, float b)
18 {
19         glClearColor(r, g, b, 1.0f);
20 }
21
22 void nex_cleardepth(float z)
23 {
24         glClearDepth(z);
25 }
26
27 void nex_clearstencil(unsigned int s)
28 {
29         glClearStencil(s);
30 }
31
32 void nex_viewport(int x, int y, int w, int h)
33 {
34         glViewport(x, y, w, h);
35 }
36
37 static unsigned int glstateopt(enum nex_state_opt opt)
38 {
39         switch(opt) {
40         case NEX_DEPTH_TEST:
41                 return GL_DEPTH_TEST;
42         case NEX_STENCIL_TEST:
43                 return GL_STENCIL_TEST;
44         case NEX_BLEND:
45                 return GL_BLEND;
46         case NEX_CULL_FACE:
47                 return GL_CULL_FACE;
48         default:
49                 break;
50         }
51         return 0;
52 }
53
54 void nex_enable(enum nex_state_opt opt)
55 {
56         unsigned int glst = glstateopt(opt);
57         if(glst > 0) {
58                 glEnable(glst);
59                 cur_state |= (1 << opt);
60         }
61 }
62
63 void nex_disable(enum nex_state_opt opt)
64 {
65         unsigned int glst = glstateopt(opt);
66         if(glst > 0) {
67                 glDisable(glst);
68                 cur_state &= ~(1 << opt);
69         }
70 }
71
72 int nex_is_enabled(enum nex_state_opt opt)
73 {
74         return cur_state & (1 << opt) ? 1 : 0;
75 }
76
77 nex_buffer *nex_alloc_buffer(size_t sz, const void *data)
78 {
79         nex_buffer *buf;
80
81         if(!(buf = malloc(sizeof *buf))) {
82                 return 0;
83         }
84         glCreateBuffers(1, &buf->bo);
85         if(data) {
86                 glNamedBufferStorage(buf->bo, sz, data, GL_DYNAMIC_STORAGE_BIT);
87         }
88
89         buf->size = sz;
90         return buf;
91 }
92
93 void nex_free_buffer(nex_buffer *buf)
94 {
95         if(!buf) return;
96         glDeleteBuffers(1, &buf->bo);
97         free(buf);
98 }
99
100 nex_geometry *nex_alloc_geometry(void)
101 {
102         nex_geometry *geom;
103
104         if(!(geom = calloc(1, sizeof *geom))) {
105                 return 0;
106         }
107         glCreateVertexArrays(1, &geom->vao);
108         return geom;
109 }
110
111 void nex_free_geometry(nex_geometry *geom)
112 {
113         if(!geom) return;
114         glDeleteVertexArrays(1, &geom->vao);
115         free(geom);
116 }
117
118 void nex_geom_vbuffer(nex_geometry *geom, unsigned int bufid, const nex_buffer *buf,
119                 unsigned int stride)
120 {
121         glVertexArrayVertexBuffer(geom->vao, bufid, buf->bo, 0, stride);
122
123         assert(bufid >= 0 && bufid < MAX_VAO_BUF);
124         geom->buf[bufid] = buf;
125 }
126
127 void nex_geom_ibuffer(nex_geometry *geom, const nex_buffer *buf)
128 {
129         glVertexArrayElementBuffer(geom->vao, buf->bo);
130
131         geom->ibuf = buf;
132 }
133
134 static int attrnelem(enum nex_vattr_type atype)
135 {
136         switch(atype) {
137         case NEX_FLOAT:
138                 return 1;
139         case NEX_VEC2:
140                 return 2;
141         case NEX_VEC3:
142         case NEX_COL3:
143                 return 3;
144         case NEX_VEC4:
145         case NEX_COL4:
146                 return 4;
147         default:
148                 break;
149         }
150         return 0;
151 }
152
153 static int attrnorm(enum nex_vattr_type atype)
154 {
155         return atype > NEX_COL3 ? 1 : 0;
156 }
157
158 static int attrsize(enum nex_vattr_type atype)
159 {
160         switch(atype) {
161         case NEX_FLOAT:
162                 return sizeof(float);
163         case NEX_VEC2:
164                 return 2 * sizeof(float);
165         case NEX_VEC3:
166         case NEX_COL3:
167                 return 3 * sizeof(float);
168         case NEX_VEC4:
169         case NEX_COL4:
170                 return 4 * sizeof(float);
171         default:
172                 break;
173         }
174         return 0;
175 }
176
177 void nex_geom_vattr(nex_geometry *geom, unsigned int attr, enum nex_vattr_type type,
178                 int bufid, unsigned int offs)
179 {
180         assert(attr >= 0 && attr < MAX_VAO_ATTR);
181
182         if(bufid >= 0) {
183                 glVertexArrayAttribFormat(geom->vao, attr, attrnelem(type), GL_FLOAT,
184                                 attrnorm(type), offs);
185                 glVertexArrayAttribBinding(geom->vao, attr, bufid);
186                 glEnableVertexArrayAttrib(geom->vao, attr);
187
188                 assert(geom->buf[bufid]);
189                 geom->attr[attr].type = type;
190                 geom->attr[attr].bufid = bufid;
191                 geom->attr[attr].size = attrsize(type);
192                 /* XXX no attempt to validate equal vcount across all bound buffers */
193                 geom->vcount = geom->buf[bufid]->size / geom->attr[attr].size;
194         } else {
195                 glDisableVertexArrayAttrib(geom->vao, attr);
196                 geom->attr[attr].bufid = 0;
197                 geom->attr[attr].size = 0;
198         }
199 }
200
201 static unsigned int glprim(enum nex_primitive prim)
202 {
203         switch(prim) {
204         case NEX_POINTS:
205                 return GL_POINTS;
206         case NEX_LINES:
207                 return GL_LINES;
208         case NEX_LINE_STRIP:
209                 return GL_LINE_STRIP;
210         case NEX_TRIANGLES:
211                 return GL_TRIANGLES;
212         case NEX_TRIANGLE_STRIP:
213                 return GL_TRIANGLE_STRIP;
214         case NEX_TRIANGLE_FAN:
215                 return GL_TRIANGLE_FAN;
216         default:
217                 break;
218         }
219         return 0;
220 }
221
222 void nex_draw_geometry(const nex_geometry *geom, enum nex_primitive prim, unsigned int count)
223 {
224         glBindVertexArray(geom->vao);
225         if(geom->ibuf) {
226                 if(!count) count = geom->ibuf->size >> 2;
227                 glDrawElements(glprim(prim), count, GL_UNSIGNED_INT, 0);
228         } else {
229                 if(!count) count = geom->vcount;
230                 glDrawArrays(glprim(prim), 0, count);
231         }
232 }
233
234 static unsigned int gl_sdrtype(enum nex_sdr_type type)
235 {
236         switch(type) {
237         case NEX_SDR_VERTEX:
238                 return GL_VERTEX_SHADER;
239         case NEX_SDR_PIXEL:
240                 return GL_FRAGMENT_SHADER;
241         default:
242                 break;
243         }
244         return 0;
245 }
246
247 nex_shader *nex_alloc_shader(enum nex_sdr_type type)
248 {
249         nex_shader *sdr;
250
251         if(!(sdr = calloc(1, sizeof *sdr))) {
252                 return 0;
253         }
254         sdr->sdr = glCreateShader(gl_sdrtype(type));
255         sdr->type = type;
256         return sdr;
257 }
258
259 void nex_free_shader(nex_shader *sdr)
260 {
261         if(!sdr || --sdr->nref > 0) return;
262
263         free(sdr->name);
264         glDeleteShader(sdr->sdr);
265         free(sdr);
266 }
267
268 nex_sdrprog *nex_alloc_sdrprog(void)
269 {
270         nex_sdrprog *prog;
271
272         if(!(prog = calloc(1, sizeof *prog))) {
273                 return 0;
274         }
275         prog->prog = glCreateProgram();
276         return prog;
277 }
278
279 void nex_free_sdrprog(nex_sdrprog *prog)
280 {
281         int i;
282
283         if(!prog) return;
284
285         glDeleteProgram(prog->prog);
286
287         for(i=0; i<prog->num_sdr; i++) {
288                 nex_free_shader(prog->sdr[i]);
289         }
290 }
291
292 void nex_sdrname(nex_shader *sdr, const char *name)
293 {
294         free(sdr->name);
295         sdr->name = strdup(name);
296 }
297
298 void nex_sdrsrc(nex_shader *sdr, const char *src)
299 {
300         glShaderSource(sdr->sdr, 1, &src, 0);
301         sdr->src = 1;
302         sdr->compiled = 0;
303 }
304
305 void nex_sdrbin(nex_shader *sdr, void *bin, size_t sz)
306 {
307         glShaderBinary(1, &sdr->sdr, GL_SHADER_BINARY_FORMAT_SPIR_V, bin, sz);
308         sdr->src = 0;
309         sdr->num_const = 0;
310         sdr->compiled = 0;
311 }
312
313 #define NEX_SDRCONST(sdr, id, val) \
314         int n; \
315         assert((sdr)->num_const < MAX_SDR_CONST); \
316         n = (sdr)->num_const++; \
317         (sdr)->cidx[n] = id; \
318         (sdr)->cval[n] = *(unsigned int*)&(val)
319
320 void nex_sdrconst_int(nex_shader *sdr, int id, int val)
321 {
322         NEX_SDRCONST(sdr, id, val);
323 }
324
325 void nex_sdrconst_float(nex_shader *sdr, int id, float val)
326 {
327         NEX_SDRCONST(sdr, id, val);
328 }
329
330 int nex_build_shader(nex_shader *sdr)
331 {
332         int status, len;
333         char *buf;
334
335         if(sdr->compiled) return 0;
336
337         if(sdr->src) {
338                 glCompileShader(sdr->sdr);
339         } else {
340                 glSpecializeShaderARB(sdr->sdr, "main", sdr->num_const, sdr->cidx, sdr->cval);
341         }
342
343         glGetShaderiv(sdr->sdr, GL_COMPILE_STATUS, &status);
344         glGetShaderInfoLog(sdr->sdr, 0, &len, 0);
345         if(len > 0) {
346                 buf = alloca(len + 1);
347                 glGetShaderInfoLog(sdr->sdr, len, 0, buf);
348                 buf[len] = 0;
349                 fprintf(status ? stdout : stderr, "nex_build_shader %s:\n%s\n",
350                                 sdr->name ? sdr->name : "<unk>", buf);
351         }
352
353         if(status) {
354                 sdr->compiled = 1;
355                 return 0;
356         }
357         return -1;
358 }
359
360 void nex_attach_shader(nex_sdrprog *prog, nex_shader *sdr)
361 {
362         assert(prog->num_sdr < MAX_SDRPROG_SDR);
363
364         glAttachShader(prog->prog, sdr->sdr);
365         prog->sdr[prog->num_sdr++] = sdr;
366         sdr->nref++;
367 }
368
369 int nex_build_sdrprog(nex_sdrprog *prog)
370 {
371         int i, status, len;
372         char *buf;
373
374         for(i=0; i<prog->num_sdr; i++) {
375                 if(!prog->sdr[i]->compiled) {
376                         if(nex_build_shader(prog->sdr[i]) == -1) {
377                                 return -1;
378                         }
379                 }
380         }
381
382         glLinkProgram(prog->prog);
383
384         glGetProgramiv(prog->prog, GL_LINK_STATUS, &status);
385         glGetProgramInfoLog(prog->prog, 0, &len, 0);
386         if(len) {
387                 buf = alloca(len + 1);
388                 glGetProgramInfoLog(prog->prog, len, 0, buf);
389                 buf[len] = 0;
390                 fprintf(status ? stdout : stderr, "nex_build_sdrprog:\n%s\n", buf);
391         }
392
393         return status ? 0 : -1;
394 }
395
396 void nex_bind_sdrprog(nex_sdrprog *prog)
397 {
398         glUseProgram(prog->prog);
399 }
400
401 int nex_find_uniform(nex_sdrprog *prog, const char *name)
402 {
403         return glGetUniformLocation(prog->prog, name);
404 }
405
406 void nex_uniform_mat4(nex_sdrprog *prog, int loc, const float *mat)
407 {
408         if(loc < 0) return;
409         glProgramUniformMatrix4fv(prog->prog, loc, 1, 0, mat);
410 }
411
412 void nex_uniform_mat4_name(nex_sdrprog *prog, const char *name, const float *mat)
413 {
414         int loc = glGetUniformLocation(prog->prog, name);
415         if(loc >= 0) {
416                 glProgramUniformMatrix4fv(prog->prog, loc, 1, 0, mat);
417         }
418 }
419
420 #define SPIRV_MAGIC             0x07230203
421 #define SPIRV_CIGAM             0x03022307
422 struct spirv_header {
423         uint32_t magic;
424         uint32_t ver;
425         uint32_t gen;
426         uint32_t bound;
427         uint32_t rsvd;
428 } __attribute__((packed));
429
430 nex_shader *nex_load_shader(const char *path, enum nex_sdr_type type)
431 {
432         FILE *fp;
433         nex_shader *sdr = 0;
434         size_t len;
435         char *buf;
436         struct spirv_header *hdr;
437
438         if(!(fp = fopen(path, "rb"))) {
439                 fprintf(stderr, "failed to open shader file: %s\n", path);
440                 return 0;
441         }
442         fseek(fp, 0, SEEK_END);
443         len = ftell(fp);
444         rewind(fp);
445
446         if(!(buf = malloc(len + 1))) {
447                 fprintf(stderr, "failed to allocate shader buffer\n");
448                 goto end;
449         }
450         if(fread(buf, 1, len, fp) != len) {
451                 fprintf(stderr, "failed to read shader\n");
452                 goto end;
453         }
454
455         if(!(sdr = nex_alloc_shader(type))) {
456                 fprintf(stderr, "failed to allocate shader\n");
457                 goto end;
458         }
459         nex_sdrname(sdr, path);
460
461         hdr = (struct spirv_header*)buf;
462         if(hdr->magic == SPIRV_MAGIC || hdr->magic == SPIRV_CIGAM) {
463                 /* TODO parse spir-v OpEntryPoint to auto-detect shader type */
464                 nex_sdrbin(sdr, buf, len);
465         } else {
466                 buf[len] = 0;
467                 nex_sdrsrc(sdr, buf);
468         }
469
470 end:
471         free(buf);
472         fclose(fp);
473         return sdr;
474 }
475
476 nex_sdrprog *nex_load_sdrprog(const char *vpath, const char *ppath)
477 {
478         nex_sdrprog *prog = 0;
479         nex_shader *vsdr = 0, *psdr = 0;
480
481         if(!(vsdr = nex_load_shader(vpath, NEX_SDR_VERTEX)) || nex_build_shader(vsdr) == -1) {
482                 goto err;
483         }
484
485         if(!(psdr = nex_load_shader(ppath, NEX_SDR_PIXEL)) || nex_build_shader(psdr) == -1) {
486                 goto err;
487         }
488
489         if(!(prog = nex_alloc_sdrprog())) {
490                 fprintf(stderr, "failed to allocate shader program\n");
491                 goto err;
492         }
493         nex_attach_shader(prog, vsdr);
494         nex_attach_shader(prog, psdr);
495         if(nex_build_sdrprog(prog) == -1) {
496                 goto err;
497         }
498
499         return prog;
500 err:
501         if(prog) {
502                 nex_free_sdrprog(prog); /* will also delete shaders */
503         } else {
504                 nex_free_shader(vsdr);
505                 nex_free_shader(psdr);
506         }
507         return 0;
508 }