https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gl_spirv.txt
[gl4] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <stddef.h>
5 #include <assert.h>
6 #include <GL/freeglut.h>
7 #include <GL/glext.h>
8 #include <GL/glx.h>
9 #include "matrix.h"
10
11 #define SPIRV
12
13 enum {
14         UBLOCK_MATRIX,
15 };
16
17 enum {
18         VATTR_VERTEX,
19         VATTR_NORMAL,
20         VATTR_TEXCOORD
21 };
22
23 struct vertex {
24         float x, y, z;
25         float nx, ny, nz;
26         float tu, tv;
27 };
28
29 struct mesh {
30         struct vertex *varr;
31         unsigned int *iarr;
32         int vcount, icount;
33
34         unsigned int vbo, ibo, vao;
35 };
36
37 struct matrix_state {
38         float view_mat[16];
39         float proj_mat[16];
40         float mvmat[16];
41         float mvpmat[16];
42         float lpos[3];
43 } __attribute__((packed));
44
45 unsigned int sampler;
46 unsigned int tex;
47
48 int init(void);
49 void cleanup(void);
50 void display(void);
51 void reshape(int x, int y);
52 void keypress(unsigned char key, int x, int y);
53 void mouse(int bn, int st, int x, int y);
54 void motion(int x, int y);
55
56 int gen_torus(struct mesh *mesh, float rad, float rrad, int usub, int vsub);
57 void draw_mesh(struct mesh *mesh);
58 unsigned int gen_texture(int width, int height);
59
60 unsigned int load_shader(const char *fname, int type);
61 unsigned int load_program(const char *vfname, const char *pfname);
62 int link_program(unsigned int prog);
63
64 void GLAPIENTRY gldebug(GLenum src, GLenum type, GLuint id, GLenum severity,
65                 GLsizei len, const char *msg, const void *cls);
66
67 float cam_theta, cam_phi = 25, cam_dist = 4;
68 int prev_x, prev_y, bnstate[8];
69
70 struct mesh torus;
71
72 unsigned int sdr;
73
74 struct matrix_state matrix_state;
75
76 unsigned int ubo_matrix;
77
78 static PFNGLSPECIALIZESHADERPROC gl_specialize_shader;
79
80 int main(int argc, char **argv)
81 {
82         glutInit(&argc, argv);
83         glutInitWindowSize(800, 600);
84         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
85         glutInitContextProfile(GLUT_CORE_PROFILE);
86         glutInitContextVersion(4, 4);
87         glutCreateWindow("GL4 test");
88
89         glutDisplayFunc(display);
90         glutReshapeFunc(reshape);
91         glutKeyboardFunc(keypress);
92         glutMouseFunc(mouse);
93         glutMotionFunc(motion);
94
95         if(init() == -1) {
96                 return 1;
97         }
98         atexit(cleanup);
99
100         glutMainLoop();
101         return 0;
102 }
103
104 int init(void)
105 {
106         glDebugMessageCallback(gldebug, 0);
107         glEnable(GL_DEPTH_TEST);
108         glEnable(GL_CULL_FACE);
109
110         gl_specialize_shader = (PFNGLSPECIALIZESHADERPROC)glXGetProcAddress((unsigned char*)"glSpecializeShaderARB");
111         if(!gl_specialize_shader) {
112                 fprintf(stderr, "failed to load glSpecializeShaderARB entry point\n");
113                 return -1;
114         }
115
116         if(!(tex = gen_texture(256, 256))) {
117                 return -1;
118         }
119
120         glGenSamplers(1, &sampler);
121         glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
122         glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
123
124         if(gen_torus(&torus, 1.0, 0.25, 32, 12) == -1) {
125                 return -1;
126         }
127
128 #ifndef SPIRV
129         if(!(sdr = load_program("vertex.glsl", "pixel.glsl"))) {
130                 return -1;
131         }
132 #else
133         if(!(sdr = load_program("spirv/vertex.spv", "spirv/pixel.spv"))) {
134                 return -1;
135         }
136 #endif
137
138         if(link_program(sdr) == -1) {
139                 fprintf(stderr, "failed to bind attribute locations\n");
140                 return -1;
141         }
142
143         glUseProgram(sdr);
144
145         glGenBuffers(1, &ubo_matrix);
146         glBindBuffer(GL_UNIFORM_BUFFER, ubo_matrix);
147         glBufferData(GL_UNIFORM_BUFFER, sizeof matrix_state, &matrix_state, GL_STREAM_DRAW);
148
149         return 0;
150 }
151
152 void cleanup(void)
153 {
154         free(torus.varr);
155         free(torus.iarr);
156         if(torus.vbo) {
157                 glDeleteBuffers(1, &torus.vbo);
158                 glDeleteBuffers(1, &torus.ibo);
159         }
160         if(torus.vao) {
161                 glDeleteVertexArrays(1, &torus.vao);
162         }
163         glDeleteTextures(1, &tex);
164         glDeleteSamplers(1, &sampler);
165 }
166
167 void display(void)
168 {
169         matrix_state.lpos[0] = -10;
170         matrix_state.lpos[1] = 10;
171         matrix_state.lpos[2] = 10;
172
173         glClearColor(0.05, 0.05, 0.05, 1.0);
174         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
175
176         mat_identity(matrix_state.view_mat);
177         mat_translate(matrix_state.view_mat, 0, 0, -cam_dist);
178         mat_rotate(matrix_state.view_mat, cam_phi, 1, 0, 0);
179         mat_rotate(matrix_state.view_mat, cam_theta, 0, 1, 0);
180
181         mat_copy(matrix_state.mvmat, matrix_state.view_mat);
182
183         mat_copy(matrix_state.mvpmat, matrix_state.proj_mat);
184         mat_mul(matrix_state.mvpmat, matrix_state.mvmat);
185
186         mat_transform(matrix_state.view_mat, matrix_state.lpos);
187
188         glUseProgram(sdr);
189
190         glBindBuffer(GL_UNIFORM_BUFFER, ubo_matrix);
191         glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof matrix_state, &matrix_state);
192         glBindBufferBase(GL_UNIFORM_BUFFER, UBLOCK_MATRIX, ubo_matrix);
193
194         glBindTexture(GL_TEXTURE_2D, tex);
195         glBindSampler(0, sampler);
196         draw_mesh(&torus);
197         glBindSampler(0, 0);
198
199         assert(glGetError() == GL_NO_ERROR);
200         glutSwapBuffers();
201 }
202
203 void reshape(int x, int y)
204 {
205         glViewport(0, 0, x, y);
206
207         mat_identity(matrix_state.proj_mat);
208         mat_perspective(matrix_state.proj_mat, 50.0, (float)x / (float)y, 0.5, 500.0);
209 }
210
211 void keypress(unsigned char key, int x, int y)
212 {
213         switch(key) {
214         case 27:
215                 exit(0);
216         }
217 }
218
219 void mouse(int bn, int st, int x, int y)
220 {
221         bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN ? 1 : 0;
222         prev_x = x;
223         prev_y = y;
224 }
225
226 void motion(int x, int y)
227 {
228         int dx = x - prev_x;
229         int dy = y - prev_y;
230         prev_x = x;
231         prev_y = y;
232
233         if(!dx && !dy) return;
234
235         if(bnstate[0]) {
236                 cam_theta += dx * 0.5;
237                 cam_phi += dy * 0.5;
238                 if(cam_phi < -90) cam_phi = -90;
239                 if(cam_phi > 90) cam_phi = 90;
240                 glutPostRedisplay();
241         }
242         if(bnstate[2]) {
243                 cam_dist += dy * 0.1;
244                 if(cam_dist < 0.0) cam_dist = 0.0;
245                 glutPostRedisplay();
246         }
247 }
248
249 static void torus_vertex(struct vertex *vout, float rad, float rrad, float u, float v)
250 {
251         float theta = u * M_PI * 2.0;
252         float phi = v * M_PI * 2.0;
253         float rx, ry, rz, cx, cy, cz;
254
255         cx = sin(theta) * rad;
256         cy = 0.0;
257         cz = -cos(theta) * rad;
258
259         rx = -cos(phi) * rrad + rad;
260         ry = sin(phi) * rrad;
261         rz = 0.0;
262
263         vout->x = rx * sin(theta) + rz * cos(theta);
264         vout->y = ry;
265         vout->z = -rx * cos(theta) + rz * sin(theta);
266
267         vout->nx = (vout->x - cx) / rrad;
268         vout->ny = (vout->y - cy) / rrad;
269         vout->nz = (vout->z - cz) / rrad;
270
271         vout->tu = u;
272         vout->tv = v;
273 }
274
275
276 int gen_torus(struct mesh *mesh, float rad, float rrad, int usub, int vsub)
277 {
278         int i, j, uverts, vverts, nverts, nquads, ntri;
279         float u, v;
280         float du = 1.0 / (float)usub;
281         float dv = 1.0 / (float)vsub;
282         struct vertex *vptr;
283         unsigned int *iptr;
284
285         if(usub < 3) usub = 3;
286         if(vsub < 3) vsub = 3;
287
288         uverts = usub + 1;
289         vverts = vsub + 1;
290
291         nverts = uverts * vverts;
292         nquads = usub * vsub;
293         ntri = nquads * 2;
294
295         mesh->vcount = nverts;
296         mesh->icount = ntri * 3;
297
298         if(!(mesh->varr = malloc(mesh->vcount * sizeof *mesh->varr))) {
299                 fprintf(stderr, "failed to allocate vertex array for %d vertices\n", mesh->vcount);
300                 return -1;
301         }
302         vptr = mesh->varr;
303         if(!(mesh->iarr = malloc(mesh->icount * sizeof *mesh->iarr))) {
304                 fprintf(stderr, "failed to allocate index array for %d indices\n", mesh->icount);
305                 free(mesh->varr);
306                 mesh->varr = 0;
307                 return -1;
308         }
309         iptr = mesh->iarr;
310
311         u = 0.0;
312         for(i=0; i<uverts; i++) {
313                 v = 0.0;
314                 for(j=0; j<vverts; j++) {
315                         torus_vertex(vptr++, rad, rrad, u, v);
316
317                         if(i < usub && j < vsub) {
318                                 int vnum = i * vverts + j;
319                                 *iptr++ = vnum;
320                                 *iptr++ = vnum + vverts + 1;
321                                 *iptr++ = vnum + 1;
322                                 *iptr++ = vnum;
323                                 *iptr++ = vnum + vverts;
324                                 *iptr++ = vnum + vverts + 1;
325                         }
326
327                         v += dv;
328                 }
329                 u += du;
330         }
331
332         glGenBuffers(1, &mesh->vbo);
333         glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
334         glBufferData(GL_ARRAY_BUFFER, mesh->vcount * sizeof *mesh->varr, mesh->varr, GL_STATIC_DRAW);
335         glBindBuffer(GL_ARRAY_BUFFER, 0);
336
337         glGenBuffers(1, &mesh->ibo);
338         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
339         glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->icount * sizeof *mesh->iarr, mesh->iarr, GL_STATIC_DRAW);
340         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
341
342         glGenVertexArrays(1, &mesh->vao);
343         glBindVertexArray(mesh->vao);
344
345         glEnableVertexAttribArray(VATTR_VERTEX);
346         glEnableVertexAttribArray(VATTR_NORMAL);
347         glEnableVertexAttribArray(VATTR_TEXCOORD);
348
349         glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
350         glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), 0);
351         glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)offsetof(struct vertex, nx));
352         glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (void*)offsetof(struct vertex, tu));
353         glBindBuffer(GL_ARRAY_BUFFER, 0);
354
355         glBindVertexArray(0);
356         return 0;
357 }
358
359 void draw_mesh(struct mesh *mesh)
360 {
361         glBindVertexArray(mesh->vao);
362
363         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo);
364         glDrawElements(GL_TRIANGLES, mesh->icount, GL_UNSIGNED_INT, 0);
365         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
366
367         glBindVertexArray(0);
368 }
369
370 unsigned int gen_texture(int width, int height)
371 {
372         int i, j;
373         unsigned int tex;
374         unsigned char *pixels, *ptr;
375
376         if(!(pixels = malloc(width * height * 3))) {
377                 return 0;
378         }
379         ptr = pixels;
380
381         for(i=0; i<height; i++) {
382                 for(j=0; j<width; j++) {
383                         int x = i ^ j;
384                         *ptr++ = x;
385                         *ptr++ = (x << 1) & 0xff;
386                         *ptr++ = (x << 2) & 0xff;
387                 }
388         }
389
390         glGenTextures(1, &tex);
391         glBindTexture(GL_TEXTURE_2D, tex);
392         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
393         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
394         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
395         glGenerateMipmap(GL_TEXTURE_2D);
396
397         free(pixels);
398         return tex;
399 }
400
401 unsigned int load_shader(const char *fname, int type)
402 {
403         unsigned int sdr;
404         int fsz;
405         char *buf;
406         FILE *fp;
407         int status, loglen;
408
409         if(!(fp = fopen(fname, "rb"))) {
410                 fprintf(stderr, "failed to open shader: %s\n", fname);
411                 return 0;
412         }
413         fseek(fp, 0, SEEK_END);
414         fsz = ftell(fp);
415         rewind(fp);
416
417         if(!(buf = malloc(fsz + 1))) {
418                 fprintf(stderr, "failed to allocate %d bytes\n", fsz + 1);
419                 fclose(fp);
420                 return 0;
421         }
422         if(fread(buf, 1, fsz, fp) < fsz) {
423                 fprintf(stderr, "failed to read shader: %s\n", fname);
424                 free(buf);
425                 fclose(fp);
426                 return 0;
427         }
428         buf[fsz] = 0;
429         fclose(fp);
430
431         sdr = glCreateShader(type);
432
433 #ifndef SPIRV
434         glShaderSource(sdr, 1, (const char**)&buf, 0);
435         glCompileShader(sdr);
436 #else
437         glShaderBinary(1, &sdr, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, buf, fsz);
438         gl_specialize_shader(sdr, "main", 0, 0, 0);
439 #endif
440         free(buf);
441
442         glGetShaderiv(sdr, GL_COMPILE_STATUS, &status);
443         if(status) {
444                 printf("successfully compiled shader: %s\n", fname);
445         } else {
446                 printf("failed to compile shader: %s\n", fname);
447         }
448
449         glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &loglen);
450         if(loglen > 0 && (buf = malloc(loglen + 1))) {
451                 glGetShaderInfoLog(sdr, loglen, 0, buf);
452                 buf[loglen] = 0;
453                 printf("%s\n", buf);
454                 free(buf);
455         }
456
457         if(!status) {
458                 glDeleteShader(sdr);
459                 return 0;
460         }
461         return sdr;
462 }
463
464 unsigned int load_program(const char *vfname, const char *pfname)
465 {
466         unsigned int vs, ps, prog;
467
468         if(!(vs = load_shader(vfname, GL_VERTEX_SHADER))) {
469                 return 0;
470         }
471         if(!(ps = load_shader(pfname, GL_FRAGMENT_SHADER))) {
472                 glDeleteShader(vs);
473                 return 0;
474         }
475
476         prog = glCreateProgram();
477         glAttachShader(prog, vs);
478         glAttachShader(prog, ps);
479
480         if(link_program(prog) == -1) {
481                 glDeleteShader(vs);
482                 glDeleteShader(ps);
483                 glDeleteProgram(prog);
484                 return 0;
485         }
486         return prog;
487 }
488
489 int link_program(unsigned int prog)
490 {
491         int status, loglen;
492         char *buf;
493
494         glLinkProgram(prog);
495
496         glGetProgramiv(prog, GL_LINK_STATUS, &status);
497         if(status) {
498                 printf("successfully linked shader program\n");
499         } else {
500                 printf("failed to link shader program\n");
501         }
502
503         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &loglen);
504         if(loglen > 0 && (buf = malloc(loglen + 1))) {
505                 glGetProgramInfoLog(prog, loglen, 0, buf);
506                 buf[loglen] = 0;
507                 printf("%s\n", buf);
508                 free(buf);
509         }
510
511         return status ? 0 : -1;
512 }
513
514 const char *gldebug_srcstr(unsigned int src)
515 {
516         switch(src) {
517         case GL_DEBUG_SOURCE_API:
518                 return "api";
519         case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
520                 return "wsys";
521         case GL_DEBUG_SOURCE_SHADER_COMPILER:
522                 return "sdrc";
523         case GL_DEBUG_SOURCE_THIRD_PARTY:
524                 return "3rdparty";
525         case GL_DEBUG_SOURCE_APPLICATION:
526                 return "app";
527         case GL_DEBUG_SOURCE_OTHER:
528                 return "other";
529         default:
530                 break;
531         }
532         return "unknown";
533 }
534
535 const char *gldebug_typestr(unsigned int type)
536 {
537         switch(type) {
538         case GL_DEBUG_TYPE_ERROR:
539                 return "error";
540         case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
541                 return "deprecated";
542         case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
543                 return "undefined behavior";
544         case GL_DEBUG_TYPE_PORTABILITY:
545                 return "portability";
546         case GL_DEBUG_TYPE_PERFORMANCE:
547                 return "performance";
548         case GL_DEBUG_TYPE_OTHER:
549                 return "other";
550         default:
551                 break;
552         }
553         return "unknown";
554 }
555
556 void GLAPIENTRY gldebug(GLenum src, GLenum type, GLuint id, GLenum severity,
557                 GLsizei len, const char *msg, const void *cls)
558 {
559         printf("[GLDEBUG] (%s) %s: %s\n", gldebug_srcstr(src), gldebug_typestr(type), msg);
560 }
561