1/* 2 * Mesa 3-D graphics library 3 * Version: 7.9 4 * 5 * Copyright (C) 2010 LunarG Inc. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Authors: 26 * Chia-I Wu <olv@lunarg.com> 27 */ 28 29/* 30 * This demo uses EGL_KHR_image_pixmap and GL_OES_EGL_image to demonstrate 31 * texture-from-pixmap. 32 */ 33 34#include <assert.h> 35#include <stdlib.h> 36#include <stdio.h> 37#include <string.h> 38#include <unistd.h> /* for usleep */ 39#include <sys/time.h> /* for gettimeofday */ 40#include <X11/Xlib.h> 41#include <X11/Xutil.h> 42#include <X11/keysym.h> 43#include <GLES/gl.h> 44#include <GLES/glext.h> 45#include <EGL/egl.h> 46#include <EGL/eglext.h> 47 48struct app_data { 49 /* native */ 50 Display *xdpy; 51 Window canvas, cube; 52 Pixmap pix; 53 unsigned int width, height, depth; 54 GC fg, bg; 55 56 /* EGL */ 57 EGLDisplay dpy; 58 EGLContext ctx; 59 EGLSurface surf; 60 EGLImageKHR img; 61 62 /* OpenGL ES */ 63 GLenum target; 64 GLuint texture; 65 66 PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; 67 PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; 68 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; 69 70 /* app state */ 71 Bool loop; 72 Bool redraw, reshape; 73 74 struct { 75 Bool active; 76 unsigned long next_frame; /* in ms */ 77 float view_rotx; 78 float view_roty; 79 float view_rotz; 80 81 } animate; 82 83 struct { 84 Bool active; 85 int x1, y1; 86 int x2, y2; 87 } paint; 88}; 89 90static void 91gl_redraw(void) 92{ 93 const GLfloat verts[4][2] = { 94 { -1, -1 }, 95 { 1, -1 }, 96 { 1, 1 }, 97 { -1, 1 } 98 }; 99 const GLfloat texcoords[4][2] = { 100 { 0, 1 }, 101 { 1, 1 }, 102 { 1, 0 }, 103 { 0, 0 } 104 }; 105 const GLfloat faces[6][4] = { 106 { 0, 0, 1, 0 }, 107 { 90, 0, 1, 0 }, 108 { 180, 0, 1, 0 }, 109 { 270, 0, 1, 0 }, 110 { 90, 1, 0, 0 }, 111 { -90, 1, 0, 0 } 112 }; 113 GLint i; 114 115 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 116 117 glVertexPointer(2, GL_FLOAT, 0, verts); 118 glTexCoordPointer(2, GL_FLOAT, 0, texcoords); 119 120 glEnableClientState(GL_VERTEX_ARRAY); 121 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 122 123 for (i = 0; i < 6; i++) { 124 glPushMatrix(); 125 glRotatef(faces[i][0], faces[i][1], faces[i][2], faces[i][3]); 126 glTranslatef(0, 0, 1.0); 127 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 128 glPopMatrix(); 129 } 130 131 glDisableClientState(GL_VERTEX_ARRAY); 132 glDisableClientState(GL_TEXTURE_COORD_ARRAY); 133} 134 135static void 136gl_reshape(int width, int height) 137{ 138 GLfloat ar = (GLfloat) width / (GLfloat) height; 139 140 glViewport(0, 0, width, height); 141 142 glMatrixMode(GL_PROJECTION); 143 glLoadIdentity(); 144 glFrustumf(-ar, ar, -1, 1, 5.0, 60.0); 145 146 glMatrixMode(GL_MODELVIEW); 147 glLoadIdentity(); 148 glTranslatef(0.0, 0.0, -10.0); 149} 150 151static void 152app_redraw(struct app_data *data) 153{ 154 /* pixmap has changed */ 155 if (data->reshape || data->paint.active) { 156 /* 157 * The extension only states that 158 * 159 * If an application specifies an EGLImage sibling as the destination 160 * for rendering and/or pixel download operations (e.g., as an 161 * OpenGL-ES framebuffer object, glTexSubImage2D, etc.), the modified 162 * image results will be observed by all EGLImage siblings in all 163 * client API contexts. 164 * 165 * Though not required by the drivers I tested, I think the rules of 166 * "Propagating Changes to Objects" should apply here. That is, the 167 * changes made by the native engine must be completed and the resource 168 * must be re-attached. 169 */ 170 eglWaitNative(EGL_CORE_NATIVE_ENGINE); 171 data->glEGLImageTargetTexture2DOES(data->target, 172 (GLeglImageOES) data->img); 173 } 174 175 XCopyArea(data->xdpy, data->pix, data->canvas, data->fg, 176 0, 0, data->width, data->height, 0, 0); 177 178 glPushMatrix(); 179 glRotatef(data->animate.view_rotx, 1, 0, 0); 180 glRotatef(data->animate.view_roty, 0, 1, 0); 181 glRotatef(data->animate.view_rotz, 0, 0, 1); 182 gl_redraw(); 183 glPopMatrix(); 184 185 eglSwapBuffers(data->dpy, data->surf); 186} 187 188static void 189app_reshape(struct app_data *data) 190{ 191 const EGLint img_attribs[] = { 192 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, 193 EGL_NONE 194 }; 195 196 XResizeWindow(data->xdpy, data->cube, data->width, data->height); 197 XMoveWindow(data->xdpy, data->cube, data->width, 0); 198 199 if (data->img) 200 data->eglDestroyImageKHR(data->dpy, data->img); 201 if (data->pix) 202 XFreePixmap(data->xdpy, data->pix); 203 204 data->pix = XCreatePixmap(data->xdpy, data->canvas, data->width, data->height, data->depth); 205 XFillRectangle(data->xdpy, data->pix, data->bg, 0, 0, data->width, data->height); 206 207 data->img = data->eglCreateImageKHR(data->dpy, EGL_NO_CONTEXT, 208 EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer) data->pix, img_attribs); 209 210 gl_reshape(data->width, data->height); 211} 212 213static void 214app_toggle_animate(struct app_data *data) 215{ 216 data->animate.active = !data->animate.active; 217 218 if (data->animate.active) { 219 struct timeval tv; 220 221 gettimeofday(&tv, NULL); 222 data->animate.next_frame = tv.tv_sec * 1000 + tv.tv_usec / 1000; 223 } 224} 225 226static void 227app_next_event(struct app_data *data) 228{ 229 XEvent event; 230 231 data->reshape = False; 232 data->redraw = False; 233 data->paint.active = False; 234 235 if (data->animate.active) { 236 struct timeval tv; 237 unsigned long now; 238 239 gettimeofday(&tv, NULL); 240 now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 241 242 /* wait for next frame */ 243 if (!XPending(data->xdpy) && now < data->animate.next_frame) { 244 usleep((data->animate.next_frame - now) * 1000); 245 gettimeofday(&tv, NULL); 246 now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 247 } 248 249 while (now >= data->animate.next_frame) { 250 data->animate.view_rotx += 1.0; 251 data->animate.view_roty += 2.0; 252 data->animate.view_rotz += 1.5; 253 254 /* 30fps */ 255 data->animate.next_frame += 1000 / 30; 256 } 257 258 /* check again in case there were events when sleeping */ 259 if (!XPending(data->xdpy)) { 260 data->redraw = True; 261 return; 262 } 263 } 264 265 XNextEvent(data->xdpy, &event); 266 267 switch (event.type) { 268 case ConfigureNotify: 269 data->width = event.xconfigure.width / 2; 270 data->height = event.xconfigure.height; 271 data->reshape = True; 272 break; 273 case Expose: 274 data->redraw = True; 275 break; 276 case KeyPress: 277 { 278 int code; 279 280 code = XLookupKeysym(&event.xkey, 0); 281 switch (code) { 282 case XK_a: 283 app_toggle_animate(data); 284 break; 285 case XK_Escape: 286 data->loop = False; 287 break; 288 default: 289 break; 290 } 291 } 292 break; 293 case ButtonPress: 294 data->paint.x1 = data->paint.x2 = event.xbutton.x; 295 data->paint.y1 = data->paint.y2 = event.xbutton.y; 296 break; 297 case ButtonRelease: 298 data->paint.active = False; 299 break; 300 case MotionNotify: 301 data->paint.x1 = data->paint.x2; 302 data->paint.y1 = data->paint.y2; 303 data->paint.x2 = event.xmotion.x; 304 data->paint.y2 = event.xmotion.y; 305 data->paint.active = True; 306 break; 307 default: 308 break; 309 } 310 311 if (data->paint.active || data->reshape) 312 data->redraw = True; 313} 314 315static void 316app_init_gl(struct app_data *data) 317{ 318 glClearColor(0.1, 0.1, 0.3, 0.0); 319 glColor4f(1.0, 1.0, 1.0, 1.0); 320 321 glGenTextures(1, &data->texture); 322 323 glBindTexture(data->target, data->texture); 324 glTexParameteri(data->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 325 glTexParameteri(data->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 326 327 glEnable(data->target); 328 glEnable(GL_DEPTH_TEST); 329} 330 331static Bool 332app_init_exts(struct app_data *data) 333{ 334 const char *exts; 335 336 exts = eglQueryString(data->dpy, EGL_EXTENSIONS); 337 data->eglCreateImageKHR = 338 (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR"); 339 data->eglDestroyImageKHR = 340 (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR"); 341 if (!exts || !strstr(exts, "EGL_KHR_image_pixmap") || 342 !data->eglCreateImageKHR || !data->eglDestroyImageKHR) { 343 printf("EGL does not support EGL_KHR_image_pixmap\n"); 344 return False; 345 } 346 347 data->target = 0; 348 exts = (const char *) glGetString(GL_EXTENSIONS); 349 data->glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) 350 eglGetProcAddress("glEGLImageTargetTexture2DOES"); 351 if (exts && data->glEGLImageTargetTexture2DOES) { 352 if (strstr(exts, "GL_OES_EGL_image")) 353 data->target = GL_TEXTURE_2D; 354#ifdef GL_OES_EGL_image_external 355 /* prefer external texture */ 356 if (strstr(exts, "GL_OES_EGL_image_external")) 357 data->target = GL_TEXTURE_EXTERNAL_OES; 358#endif 359 } 360 361 if (!data->target) { 362 printf("OpenGL ES does not support sampling from an EGLImage\n"); 363 return False; 364 } 365 366 return True; 367} 368 369static void 370app_run(struct app_data *data) 371{ 372 Window root; 373 int x, y; 374 unsigned int border; 375 376 if (!eglMakeCurrent(data->dpy, data->surf, data->surf, data->ctx)) 377 return; 378 379 if (!app_init_exts(data)) 380 return; 381 382 printf("Draw something on the left with the mouse!\n"); 383 384 app_init_gl(data); 385 386 if (!XGetGeometry(data->xdpy, data->canvas, &root, &x, &y, 387 &data->width, &data->height, &border, &data->depth)) 388 return; 389 data->width /= 2; 390 391 app_reshape(data); 392 393 XMapWindow(data->xdpy, data->canvas); 394 XMapWindow(data->xdpy, data->cube); 395 396 app_toggle_animate(data); 397 data->loop = True; 398 399 while (data->loop) { 400 app_next_event(data); 401 402 if (data->reshape) 403 app_reshape(data); 404 if (data->paint.active) { 405 XDrawLine(data->xdpy, data->pix, data->fg, 406 data->paint.x1, data->paint.y1, 407 data->paint.x2, data->paint.y2); 408 } 409 410 if (data->redraw) 411 app_redraw(data); 412 } 413 414 eglMakeCurrent(data->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 415} 416 417static Bool 418make_x_window(struct app_data *data, const char *name, 419 int x, int y, int width, int height) 420{ 421 static const EGLint attribs[] = { 422 EGL_RED_SIZE, 1, 423 EGL_GREEN_SIZE, 1, 424 EGL_BLUE_SIZE, 1, 425 EGL_DEPTH_SIZE, 1, 426 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, 427 EGL_NONE 428 }; 429 static const EGLint ctx_attribs[] = { 430 EGL_CONTEXT_CLIENT_VERSION, 1, 431 EGL_NONE 432 }; 433 int scrnum; 434 XSetWindowAttributes attr; 435 unsigned long mask; 436 Window root; 437 Window win; 438 XVisualInfo *visInfo, visTemplate; 439 int num_visuals; 440 EGLConfig config; 441 EGLint num_configs; 442 EGLint vid; 443 444 scrnum = DefaultScreen( data->xdpy ); 445 root = RootWindow( data->xdpy, scrnum ); 446 447 if (!eglChooseConfig( data->dpy, attribs, &config, 1, &num_configs)) { 448 printf("Error: couldn't get an EGL visual config\n"); 449 exit(1); 450 } 451 452 assert(config); 453 assert(num_configs > 0); 454 455 if (!eglGetConfigAttrib(data->dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { 456 printf("Error: eglGetConfigAttrib() failed\n"); 457 exit(1); 458 } 459 460 /* The X window visual must match the EGL config */ 461 visTemplate.visualid = vid; 462 visInfo = XGetVisualInfo(data->xdpy, VisualIDMask, &visTemplate, &num_visuals); 463 if (!visInfo) { 464 printf("Error: couldn't get X visual\n"); 465 exit(1); 466 } 467 468 /* window attributes */ 469 attr.background_pixel = 0; 470 attr.border_pixel = 0; 471 attr.colormap = XCreateColormap( data->xdpy, root, visInfo->visual, AllocNone); 472 attr.event_mask = StructureNotifyMask | ExposureMask | 473 KeyPressMask | ButtonPressMask | ButtonMotionMask; 474 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 475 476 win = XCreateWindow( data->xdpy, root, 0, 0, width * 2, height, 477 0, visInfo->depth, InputOutput, 478 visInfo->visual, mask, &attr ); 479 480 /* set hints and properties */ 481 { 482 XSizeHints sizehints; 483 sizehints.x = x; 484 sizehints.y = y; 485 sizehints.width = width; 486 sizehints.height = height; 487 sizehints.flags = USSize | USPosition; 488 XSetNormalHints(data->xdpy, win, &sizehints); 489 XSetStandardProperties(data->xdpy, win, name, name, 490 None, (char **)NULL, 0, &sizehints); 491 } 492 493 data->canvas = win; 494 495 attr.event_mask = 0x0; 496 win = XCreateWindow( data->xdpy, win, width, 0, width, height, 497 0, visInfo->depth, InputOutput, 498 visInfo->visual, mask, &attr ); 499 data->cube = win; 500 501 eglBindAPI(EGL_OPENGL_ES_API); 502 503 data->ctx = eglCreateContext(data->dpy, config, EGL_NO_CONTEXT, ctx_attribs ); 504 if (!data->ctx) { 505 printf("Error: eglCreateContext failed\n"); 506 exit(1); 507 } 508 509 data->surf = eglCreateWindowSurface(data->dpy, config, data->cube, NULL); 510 if (!data->surf) { 511 printf("Error: eglCreateWindowSurface failed\n"); 512 exit(1); 513 } 514 515 XFree(visInfo); 516 517 return (data->canvas && data->cube && data->ctx && data->surf); 518} 519 520static void 521app_fini(struct app_data *data) 522{ 523 if (data->img) 524 data->eglDestroyImageKHR(data->dpy, data->img); 525 if (data->pix) 526 XFreePixmap(data->xdpy, data->pix); 527 528 if (data->fg) 529 XFreeGC(data->xdpy, data->fg); 530 if (data->bg) 531 XFreeGC(data->xdpy, data->bg); 532 533 if (data->surf) 534 eglDestroySurface(data->dpy, data->surf); 535 if (data->ctx) 536 eglDestroyContext(data->dpy, data->ctx); 537 538 if (data->cube) 539 XDestroyWindow(data->xdpy, data->cube); 540 if (data->canvas) 541 XDestroyWindow(data->xdpy, data->canvas); 542 543 if (data->dpy) 544 eglTerminate(data->dpy); 545 if (data->xdpy) 546 XCloseDisplay(data->xdpy); 547} 548 549static Bool 550app_init(struct app_data *data, int argc, char **argv) 551{ 552 XGCValues gc_vals; 553 554 memset(data, 0, sizeof(*data)); 555 556 data->xdpy = XOpenDisplay(NULL); 557 if (!data->xdpy) 558 goto fail; 559 560 data->dpy = eglGetDisplay(data->xdpy); 561 if (!data->dpy || !eglInitialize(data->dpy, NULL, NULL)) 562 goto fail; 563 564 if (!make_x_window(data, "EGLImage TFP", 0, 0, 300, 300)) 565 goto fail; 566 567 gc_vals.function = GXcopy; 568 gc_vals.foreground = WhitePixel(data->xdpy, DefaultScreen(data->xdpy)); 569 gc_vals.line_width = 3; 570 gc_vals.line_style = LineSolid; 571 gc_vals.fill_style = FillSolid; 572 573 data->fg = XCreateGC(data->xdpy, data->canvas, 574 GCFunction | GCForeground | GCLineWidth | GCLineStyle | GCFillStyle, 575 &gc_vals); 576 gc_vals.foreground = BlackPixel(data->xdpy, DefaultScreen(data->xdpy)); 577 data->bg = XCreateGC(data->xdpy, data->canvas, 578 GCFunction | GCForeground | GCLineWidth | GCLineStyle | GCFillStyle, 579 &gc_vals); 580 if (!data->fg || !data->bg) 581 goto fail; 582 583 return True; 584 585fail: 586 app_fini(data); 587 return False; 588} 589 590int 591main(int argc, char **argv) 592{ 593 struct app_data data; 594 595 if (app_init(&data, argc, argv)) { 596 app_run(&data); 597 app_fini(&data); 598 } 599 600 return 0; 601} 602