es2tri.c revision 7ec3b29a
1/**************************************************************************
2 *
3 * Copyright 2008 VMware, Inc.
4 * All Rights Reserved.
5 *
6 **************************************************************************/
7
8/*
9 * Draw a triangle with X/EGL and OpenGL ES 2.x
10 */
11
12#define USE_FULL_GL 0
13
14
15
16#include <assert.h>
17#include <math.h>
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#include <X11/Xlib.h>
22#include <X11/Xutil.h>
23#include <X11/keysym.h>
24#if USE_FULL_GL
25#include "gl_wrap.h"  /* use full OpenGL */
26#else
27#include <GLES2/gl2.h>  /* use OpenGL ES 2.x */
28#endif
29#include <EGL/egl.h>
30
31
32#define FLOAT_TO_FIXED(X)   ((X) * 65535.0)
33
34
35
36static GLfloat view_rotx = 0.0, view_roty = 0.0;
37
38static GLint u_matrix = -1;
39static GLint attr_pos = 0, attr_color = 1;
40
41
42static void
43make_z_rot_matrix(GLfloat angle, GLfloat *m)
44{
45   float c = cos(angle * M_PI / 180.0);
46   float s = sin(angle * M_PI / 180.0);
47   int i;
48   for (i = 0; i < 16; i++)
49      m[i] = 0.0;
50   m[0] = m[5] = m[10] = m[15] = 1.0;
51
52   m[0] = c;
53   m[1] = s;
54   m[4] = -s;
55   m[5] = c;
56}
57
58static void
59make_scale_matrix(GLfloat xs, GLfloat ys, GLfloat zs, GLfloat *m)
60{
61   int i;
62   for (i = 0; i < 16; i++)
63      m[i] = 0.0;
64   m[0] = xs;
65   m[5] = ys;
66   m[10] = zs;
67   m[15] = 1.0;
68}
69
70
71static void
72mul_matrix(GLfloat *prod, const GLfloat *a, const GLfloat *b)
73{
74#define A(row,col)  a[(col<<2)+row]
75#define B(row,col)  b[(col<<2)+row]
76#define P(row,col)  p[(col<<2)+row]
77   GLfloat p[16];
78   GLint i;
79   for (i = 0; i < 4; i++) {
80      const GLfloat ai0=A(i,0),  ai1=A(i,1),  ai2=A(i,2),  ai3=A(i,3);
81      P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
82      P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
83      P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
84      P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
85   }
86   memcpy(prod, p, sizeof(p));
87#undef A
88#undef B
89#undef PROD
90}
91
92
93static void
94draw(void)
95{
96   static const GLfloat verts[3][2] = {
97      { -1, -1 },
98      {  1, -1 },
99      {  0,  1 }
100   };
101   static const GLfloat colors[3][3] = {
102      { 1, 0, 0 },
103      { 0, 1, 0 },
104      { 0, 0, 1 }
105   };
106   GLfloat mat[16], rot[16], scale[16];
107
108   /* Set modelview/projection matrix */
109   make_z_rot_matrix(view_rotx, rot);
110   make_scale_matrix(0.5, 0.5, 0.5, scale);
111   mul_matrix(mat, rot, scale);
112   glUniformMatrix4fv(u_matrix, 1, GL_FALSE, mat);
113
114   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
115
116   {
117      glVertexAttribPointer(attr_pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
118      glVertexAttribPointer(attr_color, 3, GL_FLOAT, GL_FALSE, 0, colors);
119      glEnableVertexAttribArray(attr_pos);
120      glEnableVertexAttribArray(attr_color);
121
122      glDrawArrays(GL_TRIANGLES, 0, 3);
123
124      glDisableVertexAttribArray(attr_pos);
125      glDisableVertexAttribArray(attr_color);
126   }
127}
128
129
130/* new window size or exposure */
131static void
132reshape(int width, int height)
133{
134   glViewport(0, 0, (GLint) width, (GLint) height);
135}
136
137
138static void
139create_shaders(void)
140{
141   static const char *fragShaderText =
142      "precision mediump float;\n"
143      "varying vec4 v_color;\n"
144      "void main() {\n"
145      "   gl_FragColor = v_color;\n"
146      "}\n";
147   static const char *vertShaderText =
148      "uniform mat4 modelviewProjection;\n"
149      "attribute vec4 pos;\n"
150      "attribute vec4 color;\n"
151      "varying vec4 v_color;\n"
152      "void main() {\n"
153      "   gl_Position = modelviewProjection * pos;\n"
154      "   v_color = color;\n"
155      "}\n";
156
157   GLuint fragShader, vertShader, program;
158   GLint stat;
159
160   fragShader = glCreateShader(GL_FRAGMENT_SHADER);
161   glShaderSource(fragShader, 1, (const char **) &fragShaderText, NULL);
162   glCompileShader(fragShader);
163   glGetShaderiv(fragShader, GL_COMPILE_STATUS, &stat);
164   if (!stat) {
165      printf("Error: fragment shader did not compile!\n");
166      exit(1);
167   }
168
169   vertShader = glCreateShader(GL_VERTEX_SHADER);
170   glShaderSource(vertShader, 1, (const char **) &vertShaderText, NULL);
171   glCompileShader(vertShader);
172   glGetShaderiv(vertShader, GL_COMPILE_STATUS, &stat);
173   if (!stat) {
174      printf("Error: vertex shader did not compile!\n");
175      exit(1);
176   }
177
178   program = glCreateProgram();
179   glAttachShader(program, fragShader);
180   glAttachShader(program, vertShader);
181   glLinkProgram(program);
182
183   glGetProgramiv(program, GL_LINK_STATUS, &stat);
184   if (!stat) {
185      char log[1000];
186      GLsizei len;
187      glGetProgramInfoLog(program, 1000, &len, log);
188      printf("Error: linking:\n%s\n", log);
189      exit(1);
190   }
191
192   glUseProgram(program);
193
194   if (1) {
195      /* test setting attrib locations */
196      glBindAttribLocation(program, attr_pos, "pos");
197      glBindAttribLocation(program, attr_color, "color");
198      glLinkProgram(program);  /* needed to put attribs into effect */
199   }
200   else {
201      /* test automatic attrib locations */
202      attr_pos = glGetAttribLocation(program, "pos");
203      attr_color = glGetAttribLocation(program, "color");
204   }
205
206   u_matrix = glGetUniformLocation(program, "modelviewProjection");
207   printf("Uniform modelviewProjection at %d\n", u_matrix);
208   printf("Attrib pos at %d\n", attr_pos);
209   printf("Attrib color at %d\n", attr_color);
210}
211
212
213static void
214init(void)
215{
216   typedef void (*proc)();
217
218#if 1 /* test code */
219   proc p = eglGetProcAddress("glMapBufferOES");
220   assert(p);
221#endif
222
223   glClearColor(0.4, 0.4, 0.4, 0.0);
224
225   create_shaders();
226}
227
228
229/*
230 * Create an RGB, double-buffered X window.
231 * Return the window and context handles.
232 */
233static void
234make_x_window(Display *x_dpy, EGLDisplay egl_dpy,
235              const char *name,
236              int x, int y, int width, int height,
237              Window *winRet,
238              EGLContext *ctxRet,
239              EGLSurface *surfRet)
240{
241   static const EGLint attribs[] = {
242      EGL_RED_SIZE, 1,
243      EGL_GREEN_SIZE, 1,
244      EGL_BLUE_SIZE, 1,
245      EGL_DEPTH_SIZE, 1,
246      EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
247      EGL_NONE
248   };
249#if USE_FULL_GL
250   static const EGLint ctx_attribs[] = {
251       EGL_NONE
252   };
253#else
254   static const EGLint ctx_attribs[] = {
255      EGL_CONTEXT_CLIENT_VERSION, 2,
256      EGL_NONE
257   };
258#endif
259
260   int scrnum;
261   XSetWindowAttributes attr;
262   unsigned long mask;
263   Window root;
264   Window win;
265   XVisualInfo *visInfo, visTemplate;
266   int num_visuals;
267   EGLContext ctx;
268   EGLConfig config;
269   EGLint num_configs;
270   EGLint vid;
271
272   scrnum = DefaultScreen( x_dpy );
273   root = RootWindow( x_dpy, scrnum );
274
275   if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) {
276      printf("Error: couldn't get an EGL visual config\n");
277      exit(1);
278   }
279
280   assert(config);
281   assert(num_configs > 0);
282
283   if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
284      printf("Error: eglGetConfigAttrib() failed\n");
285      exit(1);
286   }
287
288   /* The X window visual must match the EGL config */
289   visTemplate.visualid = vid;
290   visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
291   if (!visInfo) {
292      printf("Error: couldn't get X visual\n");
293      exit(1);
294   }
295
296   /* window attributes */
297   attr.background_pixel = 0;
298   attr.border_pixel = 0;
299   attr.colormap = XCreateColormap( x_dpy, root, visInfo->visual, AllocNone);
300   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
301   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
302
303   win = XCreateWindow( x_dpy, root, 0, 0, width, height,
304		        0, visInfo->depth, InputOutput,
305		        visInfo->visual, mask, &attr );
306
307   /* set hints and properties */
308   {
309      XSizeHints sizehints;
310      sizehints.x = x;
311      sizehints.y = y;
312      sizehints.width  = width;
313      sizehints.height = height;
314      sizehints.flags = USSize | USPosition;
315      XSetNormalHints(x_dpy, win, &sizehints);
316      XSetStandardProperties(x_dpy, win, name, name,
317                              None, (char **)NULL, 0, &sizehints);
318   }
319
320#if USE_FULL_GL /* XXX fix this when eglBindAPI() works */
321   eglBindAPI(EGL_OPENGL_API);
322#else
323   eglBindAPI(EGL_OPENGL_ES_API);
324#endif
325
326   ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs );
327   if (!ctx) {
328      printf("Error: eglCreateContext failed\n");
329      exit(1);
330   }
331
332#if !USE_FULL_GL
333   /* test eglQueryContext() */
334   {
335      EGLint val;
336      eglQueryContext(egl_dpy, ctx, EGL_CONTEXT_CLIENT_VERSION, &val);
337      assert(val == 2);
338   }
339#endif
340
341   *surfRet = eglCreateWindowSurface(egl_dpy, config, win, NULL);
342   if (!*surfRet) {
343      printf("Error: eglCreateWindowSurface failed\n");
344      exit(1);
345   }
346
347   /* sanity checks */
348   {
349      EGLint val;
350      eglQuerySurface(egl_dpy, *surfRet, EGL_WIDTH, &val);
351      assert(val == width);
352      eglQuerySurface(egl_dpy, *surfRet, EGL_HEIGHT, &val);
353      assert(val == height);
354      assert(eglGetConfigAttrib(egl_dpy, config, EGL_SURFACE_TYPE, &val));
355      assert(val & EGL_WINDOW_BIT);
356   }
357
358   XFree(visInfo);
359
360   *winRet = win;
361   *ctxRet = ctx;
362}
363
364
365static void
366event_loop(Display *dpy, Window win,
367           EGLDisplay egl_dpy, EGLSurface egl_surf)
368{
369   while (1) {
370      int redraw = 0;
371      XEvent event;
372
373      XNextEvent(dpy, &event);
374
375      switch (event.type) {
376      case Expose:
377         redraw = 1;
378         break;
379      case ConfigureNotify:
380         reshape(event.xconfigure.width, event.xconfigure.height);
381         break;
382      case KeyPress:
383         {
384            char buffer[10];
385            int r, code;
386            code = XLookupKeysym(&event.xkey, 0);
387            if (code == XK_Left) {
388               view_roty += 5.0;
389            }
390            else if (code == XK_Right) {
391               view_roty -= 5.0;
392            }
393            else if (code == XK_Up) {
394               view_rotx += 5.0;
395            }
396            else if (code == XK_Down) {
397               view_rotx -= 5.0;
398            }
399            else {
400               r = XLookupString(&event.xkey, buffer, sizeof(buffer),
401                                 NULL, NULL);
402               if (buffer[0] == 27) {
403                  /* escape */
404                  return;
405               }
406            }
407         }
408         redraw = 1;
409         break;
410      default:
411         ; /*no-op*/
412      }
413
414      if (redraw) {
415         draw();
416         eglSwapBuffers(egl_dpy, egl_surf);
417      }
418   }
419}
420
421
422static void
423usage(void)
424{
425   printf("Usage:\n");
426   printf("  -display <displayname>  set the display to run on\n");
427   printf("  -info                   display OpenGL renderer info\n");
428}
429
430
431int
432main(int argc, char *argv[])
433{
434   const int winWidth = 300, winHeight = 300;
435   Display *x_dpy;
436   Window win;
437   EGLSurface egl_surf;
438   EGLContext egl_ctx;
439   EGLDisplay egl_dpy;
440   char *dpyName = NULL;
441   GLboolean printInfo = GL_FALSE;
442   EGLint egl_major, egl_minor;
443   int i;
444   const char *s;
445
446   for (i = 1; i < argc; i++) {
447      if (strcmp(argv[i], "-display") == 0) {
448         dpyName = argv[i+1];
449         i++;
450      }
451      else if (strcmp(argv[i], "-info") == 0) {
452         printInfo = GL_TRUE;
453      }
454      else {
455         usage();
456         return -1;
457      }
458   }
459
460   x_dpy = XOpenDisplay(dpyName);
461   if (!x_dpy) {
462      printf("Error: couldn't open display %s\n",
463	     dpyName ? dpyName : getenv("DISPLAY"));
464      return -1;
465   }
466
467   egl_dpy = eglGetDisplay(x_dpy);
468   if (!egl_dpy) {
469      printf("Error: eglGetDisplay() failed\n");
470      return -1;
471   }
472
473   if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) {
474      printf("Error: eglInitialize() failed\n");
475      return -1;
476   }
477
478   s = eglQueryString(egl_dpy, EGL_VERSION);
479   printf("EGL_VERSION = %s\n", s);
480
481   s = eglQueryString(egl_dpy, EGL_VENDOR);
482   printf("EGL_VENDOR = %s\n", s);
483
484   s = eglQueryString(egl_dpy, EGL_EXTENSIONS);
485   printf("EGL_EXTENSIONS = %s\n", s);
486
487   s = eglQueryString(egl_dpy, EGL_CLIENT_APIS);
488   printf("EGL_CLIENT_APIS = %s\n", s);
489
490   make_x_window(x_dpy, egl_dpy,
491                 "OpenGL ES 2.x tri", 0, 0, winWidth, winHeight,
492                 &win, &egl_ctx, &egl_surf);
493
494   XMapWindow(x_dpy, win);
495   if (!eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) {
496      printf("Error: eglMakeCurrent() failed\n");
497      return -1;
498   }
499
500   if (printInfo) {
501      printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
502      printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
503      printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
504      printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
505   }
506
507   init();
508
509   /* Set initial projection/viewing transformation.
510    * We can't be sure we'll get a ConfigureNotify event when the window
511    * first appears.
512    */
513   reshape(winWidth, winHeight);
514
515   event_loop(x_dpy, win, egl_dpy, egl_surf);
516
517   eglDestroyContext(egl_dpy, egl_ctx);
518   eglDestroySurface(egl_dpy, egl_surf);
519   eglTerminate(egl_dpy);
520
521
522   XDestroyWindow(x_dpy, win);
523   XCloseDisplay(x_dpy);
524
525   return 0;
526}
527