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