[Tizen] Support Autoconf 2.71
[platform/upstream/SDL.git] / test / testshader.c
1 /*
2   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely.
11 */
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <math.h>
16
17 #ifdef __EMSCRIPTEN__
18 #include <emscripten/emscripten.h>
19 #endif
20
21 #include "SDL_test_common.h"
22 #define WINDOW_WIDTH 720
23 #define WINDOW_HEIGHT 1280
24 #if defined(__IPHONEOS__) || defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__NACL__) \
25     || defined(__WINDOWS__) || defined(__LINUX__) || defined(__TIZEN__)
26 #define HAVE_OPENGLES2
27 #endif
28
29
30 #ifdef HAVE_OPENGLES2
31
32 #include "SDL_opengles2.h"
33
34 typedef struct GLES2_Context
35 {
36 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
37 #include "SDL_gles2funcs.h"
38 #undef SDL_PROC
39 } GLES2_Context;
40
41
42 static SDLTest_CommonState *state;
43 static SDL_GLContext *context = NULL;
44 static int depth = 16;
45 static GLES2_Context ctx;
46
47 static int LoadContext(GLES2_Context * data)
48 {
49 #if SDL_VIDEO_DRIVER_UIKIT
50 #define __SDL_NOGETPROCADDR__
51 #elif SDL_VIDEO_DRIVER_ANDROID
52 #define __SDL_NOGETPROCADDR__
53 #elif SDL_VIDEO_DRIVER_PANDORA
54 #define __SDL_NOGETPROCADDR__
55 #endif
56
57 #if defined __SDL_NOGETPROCADDR__
58 #define SDL_PROC(ret,func,params) data->func=func;
59 #else
60 #define SDL_PROC(ret,func,params) \
61     do { \
62         data->func = SDL_GL_GetProcAddress(#func); \
63         if ( ! data->func ) { \
64             return SDL_SetError("Couldn't load GLES2 function %s: %s", #func, SDL_GetError()); \
65         } \
66     } while ( 0 );
67 #endif /* __SDL_NOGETPROCADDR__ */
68
69 #include "SDL_gles2funcs.h"
70 #undef SDL_PROC
71     return 0;
72 }
73
74 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
75 static void
76 quit(int rc)
77 {
78     int i;
79
80     if (context != NULL) {
81         for (i = 0; i < state->num_windows; i++) {
82             if (context[i]) {
83                 SDL_GL_DeleteContext(context[i]);
84             }
85         }
86
87         SDL_free(context);
88     }
89
90     SDLTest_CommonQuit(state);
91     exit(rc);
92 }
93
94 #define GL_CHECK(x) \
95         x; \
96         { \
97           GLenum glError = ctx.glGetError(); \
98           if(glError != GL_NO_ERROR) { \
99             SDL_Log("glGetError() = %i (0x%.8x) at line %i\n", glError, glError, __LINE__); \
100             quit(1); \
101           } \
102         }
103
104 /* 
105  * Simulates desktop's glRotatef. The matrix is returned in column-major 
106  * order. 
107  */
108 static void
109 rotate_matrix(float angle, float x, float y, float z, float *r)
110 {
111     float radians, c, s, c1, u[3], length;
112     int i, j;
113
114     radians = (float)(angle * M_PI) / 180.0f;
115
116     c = SDL_cosf(radians);
117     s = SDL_sinf(radians);
118
119     c1 = 1.0f - SDL_cosf(radians);
120
121     length = (float)SDL_sqrt(x * x + y * y + z * z);
122
123     u[0] = x / length;
124     u[1] = y / length;
125     u[2] = z / length;
126
127     for (i = 0; i < 16; i++) {
128         r[i] = 0.0;
129     }
130
131     r[15] = 1.0;
132
133     for (i = 0; i < 3; i++) {
134         r[i * 4 + (i + 1) % 3] = u[(i + 2) % 3] * s;
135         r[i * 4 + (i + 2) % 3] = -u[(i + 1) % 3] * s;
136     }
137
138     for (i = 0; i < 3; i++) {
139         for (j = 0; j < 3; j++) {
140             r[i * 4 + j] += c1 * u[i] * u[j] + (i == j ? c : 0.0f);
141         }
142     }
143 }
144
145 /* 
146  * Simulates gluPerspectiveMatrix 
147  */
148 static void 
149 perspective_matrix(float fovy, float aspect, float znear, float zfar, float *r)
150 {
151     int i;
152     float f;
153
154     f = 1.0f/SDL_tanf(fovy * 0.5f);
155
156     for (i = 0; i < 16; i++) {
157         r[i] = 0.0;
158     }
159
160     r[0] = f / aspect;
161     r[5] = f;
162     r[10] = (znear + zfar) / (znear - zfar);
163     r[11] = -1.0f;
164     r[14] = (2.0f * znear * zfar) / (znear - zfar);
165     r[15] = 0.0f;
166 }
167
168 /* 
169  * Multiplies lhs by rhs and writes out to r. All matrices are 4x4 and column
170  * major. In-place multiplication is supported.
171  */
172 static void
173 multiply_matrix(float *lhs, float *rhs, float *r)
174 {
175     int i, j, k;
176     float tmp[16];
177
178     for (i = 0; i < 4; i++) {
179         for (j = 0; j < 4; j++) {
180             tmp[j * 4 + i] = 0.0;
181
182             for (k = 0; k < 4; k++) {
183                 tmp[j * 4 + i] += lhs[k * 4 + i] * rhs[j * 4 + k];
184             }
185         }
186     }
187
188     for (i = 0; i < 16; i++) {
189         r[i] = tmp[i];
190     }
191 }
192
193 /* 
194  * Create shader, load in source, compile, dump debug as necessary.
195  *
196  * shader: Pointer to return created shader ID.
197  * source: Passed-in shader source code.
198  * shader_type: Passed to GL, e.g. GL_VERTEX_SHADER.
199  */
200 void 
201 process_shader(GLuint *shader, const char * source, GLint shader_type)
202 {
203     GLint status = GL_FALSE;
204     const char *shaders[1] = { NULL };
205     char buffer[1024];
206     GLsizei length;
207
208     /* Create shader and load into GL. */
209     *shader = GL_CHECK(ctx.glCreateShader(shader_type));
210
211     shaders[0] = source;
212
213     GL_CHECK(ctx.glShaderSource(*shader, 1, shaders, NULL));
214
215     /* Clean up shader source. */
216     shaders[0] = NULL;
217
218     /* Try compiling the shader. */
219     GL_CHECK(ctx.glCompileShader(*shader));
220     GL_CHECK(ctx.glGetShaderiv(*shader, GL_COMPILE_STATUS, &status));
221
222     /* Dump debug info (source and log) if compilation failed. */
223     if(status != GL_TRUE) {
224         ctx.glGetProgramInfoLog(*shader, sizeof(buffer), &length, &buffer[0]);
225         buffer[length] = '\0';
226         SDL_Log("Shader compilation failed: %s", buffer);fflush(stderr);
227         quit(-1);
228     }
229 }
230
231 /* 3D data. Vertex range -0.5..0.5 in all axes.
232 * Z -0.5 is near, 0.5 is far. */
233 const float _vertices[] =
234 {
235     /* Front face. */
236     /* Bottom left */
237     -0.5,  0.5, -0.5,
238     0.5, -0.5, -0.5,
239     -0.5, -0.5, -0.5,
240     /* Top right */
241     -0.5,  0.5, -0.5,
242     0.5,  0.5, -0.5,
243     0.5, -0.5, -0.5,
244     /* Left face */
245     /* Bottom left */
246     -0.5,  0.5,  0.5,
247     -0.5, -0.5, -0.5,
248     -0.5, -0.5,  0.5,
249     /* Top right */
250     -0.5,  0.5,  0.5,
251     -0.5,  0.5, -0.5,
252     -0.5, -0.5, -0.5,
253     /* Top face */
254     /* Bottom left */
255     -0.5,  0.5,  0.5,
256     0.5,  0.5, -0.5,
257     -0.5,  0.5, -0.5,
258     /* Top right */
259     -0.5,  0.5,  0.5,
260     0.5,  0.5,  0.5,
261     0.5,  0.5, -0.5,
262     /* Right face */
263     /* Bottom left */
264     0.5,  0.5, -0.5,
265     0.5, -0.5,  0.5,
266     0.5, -0.5, -0.5,
267     /* Top right */
268     0.5,  0.5, -0.5,
269     0.5,  0.5,  0.5,
270     0.5, -0.5,  0.5,
271     /* Back face */
272     /* Bottom left */
273     0.5,  0.5,  0.5,
274     -0.5, -0.5,  0.5,
275     0.5, -0.5,  0.5,
276     /* Top right */
277     0.5,  0.5,  0.5,
278     -0.5,  0.5,  0.5,
279     -0.5, -0.5,  0.5,
280     /* Bottom face */
281     /* Bottom left */
282     -0.5, -0.5, -0.5,
283     0.5, -0.5,  0.5,
284     -0.5, -0.5,  0.5,
285     /* Top right */
286     -0.5, -0.5, -0.5,
287     0.5, -0.5, -0.5,
288     0.5, -0.5,  0.5,
289 };
290
291 const float _colors[] =
292 {
293     /* Front face */
294     /* Bottom left */
295     1.0, 0.0, 0.0, /* red */
296     0.0, 0.0, 1.0, /* blue */
297     0.0, 1.0, 0.0, /* green */
298     /* Top right */
299     1.0, 0.0, 0.0, /* red */
300     1.0, 1.0, 0.0, /* yellow */
301     0.0, 0.0, 1.0, /* blue */
302     /* Left face */
303     /* Bottom left */
304     1.0, 1.0, 1.0, /* white */
305     0.0, 1.0, 0.0, /* green */
306     0.0, 1.0, 1.0, /* cyan */
307     /* Top right */
308     1.0, 1.0, 1.0, /* white */
309     1.0, 0.0, 0.0, /* red */
310     0.0, 1.0, 0.0, /* green */
311     /* Top face */
312     /* Bottom left */
313     1.0, 1.0, 1.0, /* white */
314     1.0, 1.0, 0.0, /* yellow */
315     1.0, 0.0, 0.0, /* red */
316     /* Top right */
317     1.0, 1.0, 1.0, /* white */
318     0.0, 0.0, 0.0, /* black */
319     1.0, 1.0, 0.0, /* yellow */
320     /* Right face */
321     /* Bottom left */
322     1.0, 1.0, 0.0, /* yellow */
323     1.0, 0.0, 1.0, /* magenta */
324     0.0, 0.0, 1.0, /* blue */
325     /* Top right */
326     1.0, 1.0, 0.0, /* yellow */
327     0.0, 0.0, 0.0, /* black */
328     1.0, 0.0, 1.0, /* magenta */
329     /* Back face */
330     /* Bottom left */
331     0.0, 0.0, 0.0, /* black */
332     0.0, 1.0, 1.0, /* cyan */
333     1.0, 0.0, 1.0, /* magenta */
334     /* Top right */
335     0.0, 0.0, 0.0, /* black */
336     1.0, 1.0, 1.0, /* white */
337     0.0, 1.0, 1.0, /* cyan */
338     /* Bottom face */
339     /* Bottom left */
340     0.0, 1.0, 0.0, /* green */
341     1.0, 0.0, 1.0, /* magenta */
342     0.0, 1.0, 1.0, /* cyan */
343     /* Top right */
344     0.0, 1.0, 0.0, /* green */
345     0.0, 0.0, 1.0, /* blue */
346     1.0, 0.0, 1.0, /* magenta */
347 };
348
349 const char* _shader_vert_src = 
350 " attribute vec4 av4position; "
351 " attribute vec3 av3color; "
352 " uniform mat4 mvp; "
353 " varying vec3 vv3color; "
354 " void main() { "
355 "    vv3color = av3color; "
356 "    gl_Position = mvp * av4position; "
357 " } ";
358
359 const char* _shader_frag_src = 
360 " precision lowp float; "
361 " varying vec3 vv3color; "
362 " void main() { "
363 "    gl_FragColor = vec4(vv3color, 1.0); "
364 " } ";
365
366 typedef struct shader_data
367 {
368     GLuint shader_program, shader_frag, shader_vert;
369
370     GLint attr_position;
371     GLint attr_color, attr_mvp;
372
373     int angle_x, angle_y, angle_z;
374
375 } shader_data;
376
377 static void
378 Render(unsigned int width, unsigned int height, shader_data* data)
379 {
380     float matrix_rotate[16], matrix_modelview[16], matrix_perspective[16], matrix_mvp[16];
381
382     /* 
383     * Do some rotation with Euler angles. It is not a fixed axis as
384     * quaterions would be, but the effect is cool. 
385     */
386     rotate_matrix((float)data->angle_x, 1.0f, 0.0f, 0.0f, matrix_modelview);
387     rotate_matrix((float)data->angle_y, 0.0f, 1.0f, 0.0f, matrix_rotate);
388
389     multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview);
390
391     rotate_matrix((float)data->angle_z, 0.0f, 1.0f, 0.0f, matrix_rotate);
392
393     multiply_matrix(matrix_rotate, matrix_modelview, matrix_modelview);
394
395     /* Pull the camera back from the cube */
396     matrix_modelview[14] -= 2.5;
397
398     perspective_matrix(45.0f, (float)width/height, 0.01f, 100.0f, matrix_perspective);
399     multiply_matrix(matrix_perspective, matrix_modelview, matrix_mvp);
400
401     GL_CHECK(ctx.glUniformMatrix4fv(data->attr_mvp, 1, GL_FALSE, matrix_mvp));
402
403     data->angle_x += 3;
404     data->angle_y += 2;
405     data->angle_z += 1;
406
407     if(data->angle_x >= 360) data->angle_x -= 360;
408     if(data->angle_x < 0) data->angle_x += 360;
409     if(data->angle_y >= 360) data->angle_y -= 360;
410     if(data->angle_y < 0) data->angle_y += 360;
411     if(data->angle_z >= 360) data->angle_z -= 360;
412     if(data->angle_z < 0) data->angle_z += 360;
413
414     GL_CHECK(ctx.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
415     GL_CHECK(ctx.glDrawArrays(GL_TRIANGLES, 0, 36));
416 }
417
418 int done;
419 Uint32 frames;
420 shader_data *datas;
421
422 void loop()
423 {
424     SDL_Event event;
425     int i;
426     int status;
427
428     /* Check for events */
429     ++frames;
430     while (SDL_PollEvent(&event) && !done) {
431         switch (event.type) {
432         /*case SDL_WINDOWEVENT:
433             switch (event.window.event) {
434                 case SDL_WINDOWEVENT_RESIZED:
435                     for (i = 0; i < state->num_windows; ++i) {
436                         if (event.window.windowID == SDL_GetWindowID(state->windows[i])) {
437                             int w, h;
438                             status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
439                             if (status) {
440                                 SDLTest_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
441                                 break;
442                             }
443                             // Change view port to the new window dimensions 
444                             SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
445                             ctx.glViewport(0, 0, w, h);
446                             state->window_w = event.window.data1;
447                             state->window_h = event.window.data2;
448                             // Update window content 
449                             Render(event.window.data1, event.window.data2, &datas[i]);
450                             SDL_GL_SwapWindow(state->windows[i]);
451                             break;
452                         }
453                     }
454                     break;
455             }*/
456                 case SDL_KEYDOWN:
457                 if(event.key.keysym.sym == 0)
458                 {
459                         done = 1;
460                 }
461                 break;
462         }
463         SDLTest_CommonEvent(state, &event, &done);
464     }
465     if (!done) {
466       for (i = 0; i < state->num_windows; ++i) {
467           status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
468           if (status) {
469               SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
470
471               /* Continue for next window */
472               continue;
473           }
474           Render(state->window_w, state->window_h, &datas[i]);
475           SDL_GL_SwapWindow(state->windows[i]);
476       }
477     }
478 #ifdef __EMSCRIPTEN__
479     else {
480         emscripten_cancel_main_loop();
481     }
482 #endif
483 }
484 int
485 main(int argc, char *argv[])
486 {
487     int fsaa, accel;
488     int value;
489     int i;
490     SDL_DisplayMode mode;
491     Uint32 then, now;
492     int status;
493     shader_data *data;
494
495     /* Initialize parameters */
496     fsaa = 0;
497     accel = 0;
498
499     /* Initialize test framework */
500     state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
501         state->window_w = WINDOW_WIDTH;
502     state->window_h = WINDOW_HEIGHT;
503     if (!state) {
504         return 1;
505     }
506     for (i = 1; i < argc;) {
507         int consumed;
508
509         consumed = SDLTest_CommonArg(state, i);
510         if (consumed == 0) {
511             if (SDL_strcasecmp(argv[i], "--fsaa") == 0) {
512                 ++fsaa;
513                 consumed = 1;
514             } else if (SDL_strcasecmp(argv[i], "--accel") == 0) {
515                 ++accel;
516                 consumed = 1;
517             } else if (SDL_strcasecmp(argv[i], "--zdepth") == 0) {
518                 i++;
519                 if (!argv[i]) {
520                     consumed = -1;
521                 } else {
522                     depth = SDL_atoi(argv[i]);
523                     consumed = 1;
524                 }
525             } else {
526                 consumed = -1;
527             }
528         }
529         if (consumed < 0) {
530             SDL_Log ("Usage: %s %s [--fsaa] [--accel] [--zdepth %%d]\n", argv[0],
531                     SDLTest_CommonUsage(state));
532             quit(1);
533         }
534         i += consumed;
535     }
536
537     /* Set OpenGL parameters */
538     state->window_flags |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS;
539     state->gl_red_size = 5;
540     state->gl_green_size = 5;
541     state->gl_blue_size = 5;
542     state->gl_depth_size = depth;
543     state->gl_major_version = 2;
544     state->gl_minor_version = 0;
545     state->gl_profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
546
547     if (fsaa) {
548         state->gl_multisamplebuffers=1;
549         state->gl_multisamplesamples=fsaa;
550     }
551     if (accel) {
552         state->gl_accelerated=1;
553     }
554     if (!SDLTest_CommonInit(state)) {
555         quit(2);
556         return 0;
557     }
558
559     context = (SDL_GLContext *)SDL_calloc(state->num_windows, sizeof(context));
560     if (context == NULL) {
561         SDL_Log("Out of memory!\n");
562         quit(2);
563     }
564     
565     /* Create OpenGL ES contexts */
566     for (i = 0; i < state->num_windows; i++) {
567         context[i] = SDL_GL_CreateContext(state->windows[i]);
568         if (!context[i]) {
569             SDL_Log("SDL_GL_CreateContext(): %s\n", SDL_GetError());
570             quit(2);
571         }
572     }
573
574     /* Important: call this *after* creating the context */
575     if (LoadContext(&ctx) < 0) {
576         SDL_Log("Could not load GLES2 functions\n");
577         quit(2);
578         return 0;
579     }
580
581
582
583     if (state->render_flags & SDL_RENDERER_PRESENTVSYNC) {
584         SDL_GL_SetSwapInterval(1);
585     } else {
586         SDL_GL_SetSwapInterval(0);
587     }
588
589     SDL_GetCurrentDisplayMode(0, &mode);
590     SDL_Log("Screen bpp: %d\n", SDL_BITSPERPIXEL(mode.format));
591     SDL_Log("\n");
592     SDL_Log("Vendor     : %s\n", ctx.glGetString(GL_VENDOR));
593     SDL_Log("Renderer   : %s\n", ctx.glGetString(GL_RENDERER));
594     SDL_Log("Version    : %s\n", ctx.glGetString(GL_VERSION));
595     SDL_Log("Extensions : %s\n", ctx.glGetString(GL_EXTENSIONS));
596     SDL_Log("\n");
597
598     status = SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &value);
599     if (!status) {
600         SDL_Log("SDL_GL_RED_SIZE: requested %d, got %d\n", 5, value);
601     } else {
602         SDL_Log( "Failed to get SDL_GL_RED_SIZE: %s\n",
603                 SDL_GetError());
604     }
605     status = SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &value);
606     if (!status) {
607         SDL_Log("SDL_GL_GREEN_SIZE: requested %d, got %d\n", 5, value);
608     } else {
609         SDL_Log( "Failed to get SDL_GL_GREEN_SIZE: %s\n",
610                 SDL_GetError());
611     }
612     status = SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &value);
613     if (!status) {
614         SDL_Log("SDL_GL_BLUE_SIZE: requested %d, got %d\n", 5, value);
615     } else {
616         SDL_Log( "Failed to get SDL_GL_BLUE_SIZE: %s\n",
617                 SDL_GetError());
618     }
619     status = SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &value);
620     if (!status) {
621         SDL_Log("SDL_GL_DEPTH_SIZE: requested %d, got %d\n", depth, value);
622     } else {
623         SDL_Log( "Failed to get SDL_GL_DEPTH_SIZE: %s\n",
624                 SDL_GetError());
625     }
626     if (fsaa) {
627         status = SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &value);
628         if (!status) {
629             SDL_Log("SDL_GL_MULTISAMPLEBUFFERS: requested 1, got %d\n", value);
630         } else {
631             SDL_Log( "Failed to get SDL_GL_MULTISAMPLEBUFFERS: %s\n",
632                     SDL_GetError());
633         }
634         status = SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &value);
635         if (!status) {
636             SDL_Log("SDL_GL_MULTISAMPLESAMPLES: requested %d, got %d\n", fsaa,
637                    value);
638         } else {
639             SDL_Log( "Failed to get SDL_GL_MULTISAMPLESAMPLES: %s\n",
640                     SDL_GetError());
641         }
642     }
643     if (accel) {
644         status = SDL_GL_GetAttribute(SDL_GL_ACCELERATED_VISUAL, &value);
645         if (!status) {
646             SDL_Log("SDL_GL_ACCELERATED_VISUAL: requested 1, got %d\n", value);
647         } else {
648             SDL_Log( "Failed to get SDL_GL_ACCELERATED_VISUAL: %s\n",
649                     SDL_GetError());
650         }
651     }
652
653     datas = (shader_data *)SDL_calloc(state->num_windows, sizeof(shader_data));
654
655     /* Set rendering settings for each context */
656     for (i = 0; i < state->num_windows; ++i) {
657
658         int w, h;
659         status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
660         if (status) {
661             SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
662
663             /* Continue for next window */
664             continue;
665         }
666         SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
667         ctx.glViewport(0, 0, w, h);
668
669         data = &datas[i];
670         data->angle_x = 0; data->angle_y = 0; data->angle_z = 0;
671
672         /* Shader Initialization */
673         process_shader(&data->shader_vert, _shader_vert_src, GL_VERTEX_SHADER);
674         process_shader(&data->shader_frag, _shader_frag_src, GL_FRAGMENT_SHADER);
675
676         /* Create shader_program (ready to attach shaders) */
677         data->shader_program = GL_CHECK(ctx.glCreateProgram());
678
679         /* Attach shaders and link shader_program */
680         GL_CHECK(ctx.glAttachShader(data->shader_program, data->shader_vert));
681         GL_CHECK(ctx.glAttachShader(data->shader_program, data->shader_frag));
682         GL_CHECK(ctx.glLinkProgram(data->shader_program));
683
684         /* Get attribute locations of non-fixed attributes like color and texture coordinates. */
685         data->attr_position = GL_CHECK(ctx.glGetAttribLocation(data->shader_program, "av4position"));
686         data->attr_color = GL_CHECK(ctx.glGetAttribLocation(data->shader_program, "av3color"));
687
688         /* Get uniform locations */
689         data->attr_mvp = GL_CHECK(ctx.glGetUniformLocation(data->shader_program, "mvp"));
690
691         GL_CHECK(ctx.glUseProgram(data->shader_program));
692
693         /* Enable attributes for position, color and texture coordinates etc. */
694         GL_CHECK(ctx.glEnableVertexAttribArray(data->attr_position));
695         GL_CHECK(ctx.glEnableVertexAttribArray(data->attr_color));
696
697         /* Populate attributes for position, color and texture coordinates etc. */
698         GL_CHECK(ctx.glVertexAttribPointer(data->attr_position, 3, GL_FLOAT, GL_FALSE, 0, _vertices));
699         GL_CHECK(ctx.glVertexAttribPointer(data->attr_color, 3, GL_FLOAT, GL_FALSE, 0, _colors));
700
701         GL_CHECK(ctx.glEnable(GL_CULL_FACE));
702         GL_CHECK(ctx.glEnable(GL_DEPTH_TEST));
703     }
704
705     /* Main render loop */
706     frames = 0;
707     then = SDL_GetTicks();
708     done = 0;
709
710 #ifdef __EMSCRIPTEN__
711     emscripten_set_main_loop(loop, 0, 1);
712 #else
713     while (!done) {
714         loop();
715     }
716 #endif
717
718     /* Print out some timing information */
719     now = SDL_GetTicks();
720     if (now > then) {
721         SDL_Log("%2.2f frames per second\n",
722                ((double) frames * 1000) / (now - then));
723     }
724 #if !defined(__ANDROID__) && !defined(__NACL__)  
725     quit(0);
726 #endif    
727     return 0;
728 }
729
730 #else /* HAVE_OPENGLES2 */
731
732 int
733 main(int argc, char *argv[])
734 {
735     SDL_Log("No OpenGL ES support on this system\n");
736     return 1;
737 }
738
739 #endif /* HAVE_OPENGLES2 */
740
741 /* vi: set ts=4 sw=4 expandtab: */