1/*
2 * Copyright (C) 2008  Tunsgten Graphics,Inc.   All Rights Reserved.
3 */
4
5/*
6 * Test EGL Pbuffers
7 * Brian Paul
8 * August 2008
9 */
10
11
12#include <assert.h>
13#include <math.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <string.h>
17#include <X11/Xlib.h>
18#include <X11/Xutil.h>
19#include <X11/keysym.h>
20#include <GLES/gl.h>
21#include <GLES/glext.h>
22#include <EGL/egl.h>
23
24
25
26static int WinWidth = 300, WinHeight = 300;
27
28static GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0;
29
30
31static void
32Normal(GLfloat *n, GLfloat nx, GLfloat ny, GLfloat nz)
33{
34   n[0] = nx;
35   n[1] = ny;
36   n[2] = nz;
37}
38
39static void
40Vertex(GLfloat *v, GLfloat vx, GLfloat vy, GLfloat vz)
41{
42   v[0] = vx;
43   v[1] = vy;
44   v[2] = vz;
45}
46
47static void
48Texcoord(GLfloat *v, GLfloat s, GLfloat t)
49{
50   v[0] = s;
51   v[1] = t;
52}
53
54
55/* Borrowed from glut, adapted */
56static void
57draw_torus(GLfloat r, GLfloat R, GLint nsides, GLint rings)
58{
59   int i, j;
60   GLfloat theta, phi, theta1;
61   GLfloat cosTheta, sinTheta;
62   GLfloat cosTheta1, sinTheta1;
63   GLfloat ringDelta, sideDelta;
64   GLfloat varray[100][3], narray[100][3], tarray[100][2];
65   int vcount;
66
67   glVertexPointer(3, GL_FLOAT, 0, varray);
68   glNormalPointer(GL_FLOAT, 0, narray);
69   glTexCoordPointer(2, GL_FLOAT, 0, tarray);
70   glEnableClientState(GL_VERTEX_ARRAY);
71   glEnableClientState(GL_NORMAL_ARRAY);
72   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
73
74   ringDelta = 2.0 * M_PI / rings;
75   sideDelta = 2.0 * M_PI / nsides;
76
77   theta = 0.0;
78   cosTheta = 1.0;
79   sinTheta = 0.0;
80   for (i = rings - 1; i >= 0; i--) {
81      theta1 = theta + ringDelta;
82      cosTheta1 = cos(theta1);
83      sinTheta1 = sin(theta1);
84
85      vcount = 0; /* glBegin(GL_QUAD_STRIP); */
86
87      phi = 0.0;
88      for (j = nsides; j >= 0; j--) {
89         GLfloat s0, s1, t;
90         GLfloat cosPhi, sinPhi, dist;
91
92         phi += sideDelta;
93         cosPhi = cos(phi);
94         sinPhi = sin(phi);
95         dist = R + r * cosPhi;
96
97         s0 = 20.0 * theta / (2.0 * M_PI);
98         s1 = 20.0 * theta1 / (2.0 * M_PI);
99         t = 8.0 * phi / (2.0 * M_PI);
100
101         Normal(narray[vcount], cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
102         Texcoord(tarray[vcount], s1, t);
103         Vertex(varray[vcount], cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
104         vcount++;
105
106         Normal(narray[vcount], cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
107         Texcoord(tarray[vcount], s0, t);
108         Vertex(varray[vcount], cosTheta * dist, -sinTheta * dist,  r * sinPhi);
109         vcount++;
110      }
111
112      /*glEnd();*/
113      assert(vcount <= 100);
114      glDrawArrays(GL_TRIANGLE_STRIP, 0, vcount);
115
116      theta = theta1;
117      cosTheta = cosTheta1;
118      sinTheta = sinTheta1;
119   }
120
121   glDisableClientState(GL_VERTEX_ARRAY);
122   glDisableClientState(GL_NORMAL_ARRAY);
123   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
124}
125
126
127static void
128draw(void)
129{
130   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
131
132   glPushMatrix();
133   glRotatef(view_rotx, 1, 0, 0);
134   glRotatef(view_roty, 0, 1, 0);
135   glRotatef(view_rotz, 0, 0, 1);
136   glScalef(0.5, 0.5, 0.5);
137
138   draw_torus(1.0, 3.0, 30, 60);
139
140   glPopMatrix();
141
142   glFinish();
143}
144
145
146/**
147 * Draw to both the window and pbuffer and compare results.
148 */
149static void
150draw_both(EGLDisplay egl_dpy, EGLSurface egl_surf, EGLSurface egl_pbuf,
151          EGLContext egl_ctx)
152{
153   unsigned *wbuf, *pbuf;
154   int x = 100, y = 110;
155   int i, dif;
156
157   wbuf = (unsigned *) malloc(WinWidth * WinHeight * 4);
158   pbuf = (unsigned *) malloc(WinWidth * WinHeight * 4);
159
160   glPixelStorei(GL_PACK_ALIGNMENT, 1);
161
162   /* first draw to window */
163   if (!eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) {
164      printf("Error: eglMakeCurrent(window) failed\n");
165      return;
166   }
167   draw();
168   glReadPixels(0, 0, WinWidth, WinHeight, GL_RGBA, GL_UNSIGNED_BYTE, wbuf);
169   printf("Window[%d,%d] = 0x%08x\n", x, y, wbuf[y*WinWidth+x]);
170
171   eglSwapBuffers(egl_dpy, egl_surf);
172
173   /* then draw to pbuffer */
174   if (!eglMakeCurrent(egl_dpy, egl_pbuf, egl_pbuf, egl_ctx)) {
175      printf("Error: eglMakeCurrent(pbuffer) failed\n");
176      return;
177   }
178   draw();
179   glReadPixels(0, 0, WinWidth, WinHeight, GL_RGBA, GL_UNSIGNED_BYTE, pbuf);
180   printf("Pbuffer[%d,%d] = 0x%08x\n", x, y, pbuf[y*WinWidth+x]);
181
182
183   /* compare renderings */
184   for (dif = i = 0; i < WinWidth * WinHeight; i++) {
185      if (wbuf[i] != pbuf[i]) {
186         dif = 1;
187         break;
188      }
189   }
190
191   if (dif)
192      printf("Difference at %d: 0x%08x vs. 0x%08x\n", i, wbuf[i], pbuf[i]);
193   else
194      printf("Window rendering matches Pbuffer rendering!\n");
195
196   free(wbuf);
197   free(pbuf);
198}
199
200
201/* new window size or exposure */
202static void
203reshape(int width, int height)
204{
205   GLfloat ar = (GLfloat) width / (GLfloat) height;
206
207   WinWidth = width;
208   WinHeight = height;
209
210   glViewport(0, 0, (GLint) width, (GLint) height);
211
212   glMatrixMode(GL_PROJECTION);
213   glLoadIdentity();
214
215#ifdef GL_VERSION_ES_CM_1_0
216   glFrustumf(-ar, ar, -1, 1, 5.0, 60.0);
217#else
218   glFrustum(-ar, ar, -1, 1, 5.0, 60.0);
219#endif
220
221   glMatrixMode(GL_MODELVIEW);
222   glLoadIdentity();
223   glTranslatef(0.0, 0.0, -15.0);
224}
225
226
227static void
228make_texture(void)
229{
230#define SZ 64
231   GLenum Filter = GL_LINEAR;
232   GLubyte image[SZ][SZ][4];
233   GLuint i, j;
234
235   for (i = 0; i < SZ; i++) {
236      for (j = 0; j < SZ; j++) {
237         GLfloat d = (i - SZ/2) * (i - SZ/2) + (j - SZ/2) * (j - SZ/2);
238         d = sqrt(d);
239         if (d < SZ/3) {
240            image[i][j][0] = 255;
241            image[i][j][1] = 255;
242            image[i][j][2] = 255;
243            image[i][j][3] = 255;
244         }
245         else {
246            image[i][j][0] = 127;
247            image[i][j][1] = 127;
248            image[i][j][2] = 127;
249            image[i][j][3] = 255;
250         }
251      }
252   }
253
254   glActiveTexture(GL_TEXTURE0); /* unit 0 */
255   glBindTexture(GL_TEXTURE_2D, 42);
256   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SZ, SZ, 0,
257                GL_RGBA, GL_UNSIGNED_BYTE, image);
258   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Filter);
259   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Filter);
260   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
261   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
262#undef SZ
263}
264
265
266
267static void
268init(void)
269{
270   static const GLfloat red[4] = {1, 0, 0, 0};
271   static const GLfloat white[4] = {1.0, 1.0, 1.0, 1.0};
272   static const GLfloat diffuse[4] = {0.7, 0.7, 0.7, 1.0};
273   static const GLfloat specular[4] = {0.001, 0.001, 0.001, 1.0};
274   static const GLfloat pos[4] = {20, 20, 50, 1};
275
276   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
277   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white);
278   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 9.0);
279
280   glEnable(GL_LIGHTING);
281   glEnable(GL_LIGHT0);
282   glLightfv(GL_LIGHT0, GL_POSITION, pos);
283   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
284   glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
285
286   glClearColor(0.4, 0.4, 0.4, 0.0);
287   glEnable(GL_DEPTH_TEST);
288
289   make_texture();
290   glEnable(GL_TEXTURE_2D);
291}
292
293
294/*
295 * Create an RGB, double-buffered X window.
296 * Return the window and context handles.
297 */
298static void
299make_x_window(Display *x_dpy, EGLDisplay egl_dpy,
300              const char *name,
301              int x, int y, int width, int height,
302              Window *winRet,
303              EGLContext *ctxRet,
304              EGLSurface *surfRet)
305{
306   static const EGLint attribs[] = {
307      EGL_RED_SIZE, 1,
308      EGL_GREEN_SIZE, 1,
309      EGL_BLUE_SIZE, 1,
310      EGL_DEPTH_SIZE, 1,
311      EGL_NONE
312   };
313
314   int scrnum;
315   XSetWindowAttributes attr;
316   unsigned long mask;
317   Window root;
318   Window win;
319   XVisualInfo *visInfo, visTemplate;
320   int num_visuals;
321   EGLContext ctx;
322   EGLConfig config;
323   EGLint num_configs;
324   EGLint vid;
325
326   scrnum = DefaultScreen( x_dpy );
327   root = RootWindow( x_dpy, scrnum );
328
329   if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) {
330      printf("Error: couldn't get an EGL visual config\n");
331      exit(1);
332   }
333
334   assert(config);
335   assert(num_configs > 0);
336
337   if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
338      printf("Error: eglGetConfigAttrib() failed\n");
339      exit(1);
340   }
341
342   /* The X window visual must match the EGL config */
343   visTemplate.visualid = vid;
344   visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
345   if (!visInfo) {
346      printf("Error: couldn't get X visual\n");
347      exit(1);
348   }
349
350   /* window attributes */
351   attr.background_pixel = 0;
352   attr.border_pixel = 0;
353   attr.colormap = XCreateColormap( x_dpy, root, visInfo->visual, AllocNone);
354   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
355   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
356
357   win = XCreateWindow( x_dpy, root, 0, 0, width, height,
358		        0, visInfo->depth, InputOutput,
359		        visInfo->visual, mask, &attr );
360
361   /* set hints and properties */
362   {
363      XSizeHints sizehints;
364      sizehints.x = x;
365      sizehints.y = y;
366      sizehints.width  = width;
367      sizehints.height = height;
368      sizehints.flags = USSize | USPosition;
369      XSetNormalHints(x_dpy, win, &sizehints);
370      XSetStandardProperties(x_dpy, win, name, name,
371                              None, (char **)NULL, 0, &sizehints);
372   }
373
374   eglBindAPI(EGL_OPENGL_ES_API);
375
376   ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, NULL );
377   if (!ctx) {
378      printf("Error: eglCreateContext failed\n");
379      exit(1);
380   }
381
382   *surfRet = eglCreateWindowSurface(egl_dpy, config, win, NULL);
383
384   if (!*surfRet) {
385      printf("Error: eglCreateWindowSurface failed\n");
386      exit(1);
387   }
388
389   XFree(visInfo);
390
391   *winRet = win;
392   *ctxRet = ctx;
393}
394
395
396static EGLSurface
397make_pbuffer(Display *x_dpy, EGLDisplay egl_dpy, int width, int height)
398{
399   static const EGLint config_attribs[] = {
400      EGL_RED_SIZE, 1,
401      EGL_GREEN_SIZE, 1,
402      EGL_BLUE_SIZE, 1,
403      EGL_DEPTH_SIZE, 1,
404      EGL_NONE
405   };
406   EGLConfig config;
407   EGLSurface pbuf;
408   EGLint num_configs;
409   EGLint pbuf_attribs[5];
410
411   pbuf_attribs[0] = EGL_WIDTH;
412   pbuf_attribs[1] = width;
413   pbuf_attribs[2] = EGL_HEIGHT;
414   pbuf_attribs[3] = height;
415   pbuf_attribs[4] = EGL_NONE;
416
417   if (!eglChooseConfig( egl_dpy, config_attribs, &config, 1, &num_configs)) {
418      printf("Error: couldn't get an EGL config for pbuffer\n");
419      exit(1);
420   }
421
422   pbuf = eglCreatePbufferSurface(egl_dpy, config, pbuf_attribs);
423
424   return pbuf;
425}
426
427
428static void
429event_loop(Display *dpy, Window win,
430           EGLDisplay egl_dpy, EGLSurface egl_surf, EGLSurface egl_pbuf,
431           EGLContext egl_ctx)
432{
433   int anim = 0;
434
435   while (1) {
436      int redraw = 0;
437
438      if (!anim || XPending(dpy)) {
439         XEvent event;
440         XNextEvent(dpy, &event);
441
442         switch (event.type) {
443         case Expose:
444            redraw = 1;
445            break;
446         case ConfigureNotify:
447            if (event.xconfigure.window == win)
448               reshape(event.xconfigure.width, event.xconfigure.height);
449            break;
450         case KeyPress:
451            {
452               char buffer[10];
453               int r, code;
454               code = XLookupKeysym(&event.xkey, 0);
455               if (code == XK_Left) {
456               view_roty += 5.0;
457               }
458               else if (code == XK_Right) {
459                  view_roty -= 5.0;
460               }
461               else if (code == XK_Up) {
462                  view_rotx += 5.0;
463               }
464               else if (code == XK_Down) {
465                  view_rotx -= 5.0;
466               }
467               else {
468                  r = XLookupString(&event.xkey, buffer, sizeof(buffer),
469                                    NULL, NULL);
470                  if (buffer[0] == ' ') {
471                     anim = !anim;
472                  }
473                  else if (buffer[0] == 27) {
474                     /* escape */
475                     return;
476                  }
477               }
478            }
479            redraw = 1;
480            break;
481         default:
482            ; /*no-op*/
483         }
484      }
485
486      if (anim) {
487         view_rotx += 1.0;
488         view_roty += 2.0;
489         redraw = 1;
490      }
491
492      if (redraw) {
493         draw_both(egl_dpy, egl_surf, egl_pbuf, egl_ctx);
494      }
495   }
496}
497
498
499static void
500usage(void)
501{
502   printf("Usage:\n");
503   printf("  -display <displayname>  set the display to run on\n");
504   printf("  -info                   display OpenGL renderer info\n");
505}
506
507
508int
509main(int argc, char *argv[])
510{
511   Display *x_dpy;
512   Window win;
513   EGLSurface egl_surf, egl_pbuf;
514   EGLContext egl_ctx;
515   EGLDisplay egl_dpy;
516   char *dpyName = NULL;
517   GLboolean printInfo = GL_FALSE;
518   EGLint egl_major, egl_minor;
519   int i;
520   const char *s;
521
522   for (i = 1; i < argc; i++) {
523      if (strcmp(argv[i], "-display") == 0) {
524         dpyName = argv[i+1];
525         i++;
526      }
527      else if (strcmp(argv[i], "-info") == 0) {
528         printInfo = GL_TRUE;
529      }
530      else {
531         usage();
532         return -1;
533      }
534   }
535
536   x_dpy = XOpenDisplay(dpyName);
537   if (!x_dpy) {
538      printf("Error: couldn't open display %s\n",
539	     dpyName ? dpyName : getenv("DISPLAY"));
540      return -1;
541   }
542
543   egl_dpy = eglGetDisplay(x_dpy);
544   if (!egl_dpy) {
545      printf("Error: eglGetDisplay() failed\n");
546      return -1;
547   }
548
549   if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) {
550      printf("Error: eglInitialize() failed\n");
551      return -1;
552   }
553
554   s = eglQueryString(egl_dpy, EGL_VERSION);
555   printf("EGL_VERSION = %s\n", s);
556
557   s = eglQueryString(egl_dpy, EGL_VENDOR);
558   printf("EGL_VENDOR = %s\n", s);
559
560   s = eglQueryString(egl_dpy, EGL_EXTENSIONS);
561   printf("EGL_EXTENSIONS = %s\n", s);
562
563   s = eglQueryString(egl_dpy, EGL_CLIENT_APIS);
564   printf("EGL_CLIENT_APIS = %s\n", s);
565
566   make_x_window(x_dpy, egl_dpy,
567                 "pbuffer", 0, 0, WinWidth, WinHeight,
568                 &win, &egl_ctx, &egl_surf);
569
570   egl_pbuf = make_pbuffer(x_dpy, egl_dpy, WinWidth, WinHeight);
571   if (!egl_pbuf) {
572      printf("Error: eglCreatePBufferSurface() failed\n");
573      return -1;
574   }
575
576   XMapWindow(x_dpy, win);
577   if (!eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) {
578      printf("Error: eglMakeCurrent() failed\n");
579      return -1;
580   }
581
582   if (printInfo) {
583      printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
584      printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
585      printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
586      printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
587   }
588
589   init();
590
591   /* Set initial projection/viewing transformation.
592    * We can't be sure we'll get a ConfigureNotify event when the window
593    * first appears.
594    */
595   reshape(WinWidth, WinHeight);
596
597   event_loop(x_dpy, win, egl_dpy, egl_surf, egl_pbuf, egl_ctx);
598
599   eglDestroyContext(egl_dpy, egl_ctx);
600   eglDestroySurface(egl_dpy, egl_surf);
601   eglTerminate(egl_dpy);
602
603
604   XDestroyWindow(x_dpy, win);
605   XCloseDisplay(x_dpy);
606
607   return 0;
608}
609