Home | History | Annotate | Line # | Download | only in opengl
      1 /*
      2  * Copyright (C) 2000  Brian Paul   All Rights Reserved.
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice shall be included
     12  * in all copies or substantial portions of the Software.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20  *
     21  * Ported to EGL by Chia-I Wu <olvaffe (at) gmail.com>
     22  */
     23 
     24 
     25 /*
     26  * This program tests EGL thread safety.
     27  * Command line options:
     28  *  -p                       Open a display connection for each thread
     29  *  -l                       Enable application-side locking
     30  *  -n <num threads>         Number of threads to create (default is 2)
     31  *  -display <display name>  Specify X display (default is $DISPLAY)
     32  *  -t                       Use texture mapping
     33  *
     34  * Brian Paul  20 July 2000
     35  */
     36 
     37 
     38 /*
     39  * Notes:
     40  * - Each thread gets its own EGL context.
     41  *
     42  * - The EGL contexts share texture objects.
     43  *
     44  * - When 't' is pressed to update the texture image, the window/thread which
     45  *   has input focus is signalled to change the texture.  The other threads
     46  *   should see the updated texture the next time they call glBindTexture.
     47  */
     48 
     49 
     50 #if defined(PTHREADS)   /* defined by Mesa on Linux and other platforms */
     51 
     52 #include <assert.h>
     53 #include <X11/Xlib.h>
     54 #include <X11/Xutil.h>
     55 #include "gl_wrap.h"
     56 #include <EGL/egl.h>
     57 #include <math.h>
     58 #include <stdio.h>
     59 #include <stdlib.h>
     60 #include <string.h>
     61 #include <unistd.h>
     62 #include <pthread.h>
     63 
     64 
     65 /*
     66  * Each window/thread/context:
     67  */
     68 struct winthread {
     69    Display *Dpy;
     70    int Index;
     71    pthread_t Thread;
     72    Window Win;
     73    EGLDisplay Display;
     74    EGLContext Context;
     75    EGLSurface Surface;
     76    float Angle;
     77    int WinWidth, WinHeight;
     78    GLboolean NewSize;
     79    GLboolean Initialized;
     80    GLboolean MakeNewTexture;
     81 };
     82 
     83 
     84 #define MAX_WINTHREADS 100
     85 static struct winthread WinThreads[MAX_WINTHREADS];
     86 static int NumWinThreads = 0;
     87 static volatile GLboolean ExitFlag = GL_FALSE;
     88 
     89 static GLboolean MultiDisplays = 0;
     90 static GLboolean Locking = 0;
     91 static GLboolean Texture = GL_FALSE;
     92 static GLuint TexObj = 12;
     93 static GLboolean Animate = GL_TRUE;
     94 
     95 static pthread_mutex_t Mutex;
     96 static pthread_cond_t CondVar;
     97 static pthread_mutex_t CondMutex;
     98 
     99 
    100 static void
    101 Error(const char *msg)
    102 {
    103    fprintf(stderr, "Error: %s\n", msg);
    104    exit(1);
    105 }
    106 
    107 
    108 static void
    109 signal_redraw(void)
    110 {
    111    pthread_mutex_lock(&CondMutex);
    112    pthread_cond_broadcast(&CondVar);
    113    pthread_mutex_unlock(&CondMutex);
    114 }
    115 
    116 
    117 static void
    118 MakeNewTexture(struct winthread *wt)
    119 {
    120 #define TEX_SIZE 128
    121    static float step = 0.0;
    122    GLfloat image[TEX_SIZE][TEX_SIZE][4];
    123    GLint width;
    124    int i, j;
    125 
    126    for (j = 0; j < TEX_SIZE; j++) {
    127       for (i = 0; i < TEX_SIZE; i++) {
    128          float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE;
    129          float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE;
    130          float r = dt * dt + ds * ds + step;
    131          image[j][i][0] =
    132          image[j][i][1] =
    133          image[j][i][2] = 0.75 + 0.25 * cos(r);
    134          image[j][i][3] = 1.0;
    135       }
    136    }
    137 
    138    step += 0.5;
    139 
    140    glBindTexture(GL_TEXTURE_2D, TexObj);
    141 
    142    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
    143    if (width) {
    144       assert(width == TEX_SIZE);
    145       /* sub-tex replace */
    146       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE,
    147                    GL_RGBA, GL_FLOAT, image);
    148    }
    149    else {
    150       /* create new */
    151       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    152       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    153 
    154       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
    155                    GL_RGBA, GL_FLOAT, image);
    156    }
    157 }
    158 
    159 
    160 
    161 /* draw a colored cube */
    162 static void
    163 draw_object(void)
    164 {
    165    glPushMatrix();
    166    glScalef(0.75, 0.75, 0.75);
    167 
    168    glColor3f(1, 0, 0);
    169 
    170    if (Texture) {
    171       glBindTexture(GL_TEXTURE_2D, TexObj);
    172       glEnable(GL_TEXTURE_2D);
    173    }
    174    else {
    175       glDisable(GL_TEXTURE_2D);
    176    }
    177 
    178    glBegin(GL_QUADS);
    179 
    180    /* -X */
    181    glColor3f(0, 1, 1);
    182    glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
    183    glTexCoord2f(1, 0);  glVertex3f(-1,  1, -1);
    184    glTexCoord2f(1, 1);  glVertex3f(-1,  1,  1);
    185    glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
    186 
    187    /* +X */
    188    glColor3f(1, 0, 0);
    189    glTexCoord2f(0, 0);  glVertex3f(1, -1, -1);
    190    glTexCoord2f(1, 0);  glVertex3f(1,  1, -1);
    191    glTexCoord2f(1, 1);  glVertex3f(1,  1,  1);
    192    glTexCoord2f(0, 1);  glVertex3f(1, -1,  1);
    193 
    194    /* -Y */
    195    glColor3f(1, 0, 1);
    196    glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
    197    glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
    198    glTexCoord2f(1, 1);  glVertex3f( 1, -1,  1);
    199    glTexCoord2f(0, 1);  glVertex3f(-1, -1,  1);
    200 
    201    /* +Y */
    202    glColor3f(0, 1, 0);
    203    glTexCoord2f(0, 0);  glVertex3f(-1, 1, -1);
    204    glTexCoord2f(1, 0);  glVertex3f( 1, 1, -1);
    205    glTexCoord2f(1, 1);  glVertex3f( 1, 1,  1);
    206    glTexCoord2f(0, 1);  glVertex3f(-1, 1,  1);
    207 
    208    /* -Z */
    209    glColor3f(1, 1, 0);
    210    glTexCoord2f(0, 0);  glVertex3f(-1, -1, -1);
    211    glTexCoord2f(1, 0);  glVertex3f( 1, -1, -1);
    212    glTexCoord2f(1, 1);  glVertex3f( 1,  1, -1);
    213    glTexCoord2f(0, 1);  glVertex3f(-1,  1, -1);
    214 
    215    /* +Y */
    216    glColor3f(0, 0, 1);
    217    glTexCoord2f(0, 0);  glVertex3f(-1, -1, 1);
    218    glTexCoord2f(1, 0);  glVertex3f( 1, -1, 1);
    219    glTexCoord2f(1, 1);  glVertex3f( 1,  1, 1);
    220    glTexCoord2f(0, 1);  glVertex3f(-1,  1, 1);
    221 
    222    glEnd();
    223 
    224    glPopMatrix();
    225 }
    226 
    227 
    228 /* signal resize of given window */
    229 static void
    230 resize(struct winthread *wt, int w, int h)
    231 {
    232    wt->NewSize = GL_TRUE;
    233    wt->WinWidth = w;
    234    wt->WinHeight = h;
    235    if (!Animate)
    236       signal_redraw();
    237 }
    238 
    239 
    240 /*
    241  * We have an instance of this for each thread.
    242  */
    243 static void
    244 draw_loop(struct winthread *wt)
    245 {
    246    while (!ExitFlag) {
    247 
    248       if (Locking)
    249          pthread_mutex_lock(&Mutex);
    250 
    251       if (!wt->Initialized) {
    252          eglMakeCurrent(wt->Display, wt->Surface, wt->Surface, wt->Context);
    253          printf("xeglthreads: %d: GL_RENDERER = %s\n", wt->Index,
    254                 (char *) glGetString(GL_RENDERER));
    255          if (Texture /*&& wt->Index == 0*/) {
    256             MakeNewTexture(wt);
    257          }
    258          wt->Initialized = GL_TRUE;
    259       }
    260 
    261       if (Locking)
    262          pthread_mutex_unlock(&Mutex);
    263 
    264       eglBindAPI(EGL_OPENGL_API);
    265       if (eglGetCurrentContext() != wt->Context) {
    266          printf("xeglthreads: current context %p != %p\n",
    267                eglGetCurrentContext(), wt->Context);
    268       }
    269 
    270       glEnable(GL_DEPTH_TEST);
    271 
    272       if (wt->NewSize) {
    273          GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
    274          glViewport(0, 0, wt->WinWidth, wt->WinHeight);
    275          glMatrixMode(GL_PROJECTION);
    276          glLoadIdentity();
    277          glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
    278          glMatrixMode(GL_MODELVIEW);
    279          glLoadIdentity();
    280          glTranslatef(0, 0, -2.5);
    281          wt->NewSize = GL_FALSE;
    282       }
    283 
    284       if (wt->MakeNewTexture) {
    285          MakeNewTexture(wt);
    286          wt->MakeNewTexture = GL_FALSE;
    287       }
    288 
    289       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    290 
    291       glPushMatrix();
    292          glRotatef(wt->Angle, 0, 1, 0);
    293          glRotatef(wt->Angle, 1, 0, 0);
    294          glScalef(0.7, 0.7, 0.7);
    295          draw_object();
    296       glPopMatrix();
    297 
    298       if (Locking)
    299          pthread_mutex_lock(&Mutex);
    300 
    301       eglSwapBuffers(wt->Display, wt->Surface);
    302 
    303       if (Locking)
    304          pthread_mutex_unlock(&Mutex);
    305 
    306       if (Animate) {
    307          usleep(5000);
    308       }
    309       else {
    310          /* wait for signal to draw */
    311          pthread_mutex_lock(&CondMutex);
    312          pthread_cond_wait(&CondVar, &CondMutex);
    313          pthread_mutex_unlock(&CondMutex);
    314       }
    315       wt->Angle += 1.0;
    316    }
    317    eglMakeCurrent(wt->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    318 }
    319 
    320 
    321 static void
    322 keypress(XEvent *event, struct winthread *wt)
    323 {
    324    char buf[100];
    325    KeySym keySym;
    326    XComposeStatus stat;
    327 
    328    XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat);
    329 
    330    switch (keySym) {
    331    case XK_Escape:
    332       /* tell all threads to exit */
    333       if (!Animate) {
    334          signal_redraw();
    335       }
    336       ExitFlag = GL_TRUE;
    337       /*printf("exit draw_loop %d\n", wt->Index);*/
    338       return;
    339    case XK_t:
    340    case XK_T:
    341       if (Texture) {
    342          wt->MakeNewTexture = GL_TRUE;
    343          if (!Animate)
    344             signal_redraw();
    345       }
    346       break;
    347    case XK_a:
    348    case XK_A:
    349       Animate = !Animate;
    350       if (Animate)  /* yes, prev Animate state! */
    351          signal_redraw();
    352       break;
    353    case XK_s:
    354    case XK_S:
    355       if (!Animate)
    356          signal_redraw();
    357       break;
    358    default:
    359       ; /* nop */
    360    }
    361 }
    362 
    363 
    364 /*
    365  * The main process thread runs this loop.
    366  * Single display connection for all threads.
    367  */
    368 static void
    369 event_loop(Display *dpy)
    370 {
    371    XEvent event;
    372    int i;
    373 
    374    assert(!MultiDisplays);
    375 
    376    while (!ExitFlag) {
    377 
    378       if (Locking) {
    379          while (1) {
    380             int k;
    381             pthread_mutex_lock(&Mutex);
    382             k = XPending(dpy);
    383             if (k) {
    384                XNextEvent(dpy, &event);
    385                pthread_mutex_unlock(&Mutex);
    386                break;
    387             }
    388             pthread_mutex_unlock(&Mutex);
    389             usleep(5000);
    390          }
    391       }
    392       else {
    393          XNextEvent(dpy, &event);
    394       }
    395 
    396       switch (event.type) {
    397          case ConfigureNotify:
    398             /* Find winthread for this event's window */
    399             for (i = 0; i < NumWinThreads; i++) {
    400                struct winthread *wt = &WinThreads[i];
    401                if (event.xconfigure.window == wt->Win) {
    402                   resize(wt, event.xconfigure.width,
    403                          event.xconfigure.height);
    404                   break;
    405                }
    406             }
    407             break;
    408          case KeyPress:
    409             for (i = 0; i < NumWinThreads; i++) {
    410                struct winthread *wt = &WinThreads[i];
    411                if (event.xkey.window == wt->Win) {
    412                   keypress(&event, wt);
    413                   break;
    414                }
    415             }
    416             break;
    417          default:
    418             /*no-op*/ ;
    419       }
    420    }
    421 }
    422 
    423 
    424 /*
    425  * Separate display connection for each thread.
    426  */
    427 static void
    428 event_loop_multi(void)
    429 {
    430    XEvent event;
    431    int w = 0;
    432 
    433    assert(MultiDisplays);
    434 
    435    while (!ExitFlag) {
    436       struct winthread *wt = &WinThreads[w];
    437       if (XPending(wt->Dpy)) {
    438          XNextEvent(wt->Dpy, &event);
    439          switch (event.type) {
    440          case ConfigureNotify:
    441             resize(wt, event.xconfigure.width, event.xconfigure.height);
    442             break;
    443          case KeyPress:
    444             keypress(&event, wt);
    445             break;
    446          default:
    447             ; /* nop */
    448          }
    449       }
    450       w = (w + 1) % NumWinThreads;
    451       usleep(5000);
    452    }
    453 }
    454 
    455 
    456 
    457 /*
    458  * we'll call this once for each thread, before the threads are created.
    459  */
    460 static void
    461 create_window(struct winthread *wt, EGLContext shareCtx)
    462 {
    463    Window win;
    464    EGLContext ctx;
    465    EGLSurface surf;
    466    EGLint attribs[] = { EGL_RED_SIZE, 1,
    467                         EGL_GREEN_SIZE, 1,
    468                         EGL_BLUE_SIZE, 1,
    469                         EGL_DEPTH_SIZE, 1,
    470                         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
    471                         EGL_NONE };
    472    EGLConfig config;
    473    EGLint num_configs;
    474    EGLint vid;
    475    int scrnum;
    476    XSetWindowAttributes attr;
    477    unsigned long mask;
    478    Window root;
    479    XVisualInfo *visinfo, visTemplate;
    480    int num_visuals;
    481    int width = 160, height = 160;
    482    int xpos = (wt->Index % 8) * (width + 10);
    483    int ypos = (wt->Index / 8) * (width + 20);
    484 
    485    scrnum = DefaultScreen(wt->Dpy);
    486    root = RootWindow(wt->Dpy, scrnum);
    487 
    488    if (!eglChooseConfig(wt->Display, attribs, &config, 1, &num_configs) ||
    489        !num_configs) {
    490       Error("Unable to choose an EGL config");
    491    }
    492 
    493    assert(config);
    494    assert(num_configs > 0);
    495 
    496    if (!eglGetConfigAttrib(wt->Display, config, EGL_NATIVE_VISUAL_ID, &vid)) {
    497       Error("Unable to get visual id of EGL config\n");
    498    }
    499 
    500    visTemplate.visualid = vid;
    501    visinfo = XGetVisualInfo(wt->Dpy, VisualIDMask,
    502                         &visTemplate, &num_visuals);
    503    if (!visinfo) {
    504       Error("Unable to find RGB, Z, double-buffered visual");
    505    }
    506 
    507    /* window attributes */
    508    attr.background_pixel = 0;
    509    attr.border_pixel = 0;
    510    attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
    511    attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
    512    mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
    513 
    514    win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
    515                         0, visinfo->depth, InputOutput,
    516                         visinfo->visual, mask, &attr);
    517    if (!win) {
    518       Error("Couldn't create window");
    519    }
    520 
    521    XFree(visinfo);
    522 
    523    {
    524       XSizeHints sizehints;
    525       sizehints.x = xpos;
    526       sizehints.y = ypos;
    527       sizehints.width  = width;
    528       sizehints.height = height;
    529       sizehints.flags = USSize | USPosition;
    530       XSetNormalHints(wt->Dpy, win, &sizehints);
    531       XSetStandardProperties(wt->Dpy, win, "xeglthreads", "xeglthreads",
    532                               None, (char **)NULL, 0, &sizehints);
    533    }
    534 
    535    eglBindAPI(EGL_OPENGL_API);
    536 
    537    ctx = eglCreateContext(wt->Display, config, shareCtx, NULL);
    538    if (!ctx) {
    539       Error("Couldn't create EGL context");
    540    }
    541    surf = eglCreateWindowSurface(wt->Display, config, win, NULL);
    542    if (!surf) {
    543       Error("Couldn't create EGL surface");
    544    }
    545 
    546    XMapWindow(wt->Dpy, win);
    547    XSync(wt->Dpy, 0);
    548 
    549    /* save the info for this window/context */
    550    wt->Win = win;
    551    wt->Context = ctx;
    552    wt->Surface = surf;
    553    wt->Angle = 0.0;
    554    wt->WinWidth = width;
    555    wt->WinHeight = height;
    556    wt->NewSize = GL_TRUE;
    557 }
    558 
    559 
    560 /*
    561  * Called by pthread_create()
    562  */
    563 static void *
    564 thread_function(void *p)
    565 {
    566    struct winthread *wt = (struct winthread *) p;
    567    draw_loop(wt);
    568    return NULL;
    569 }
    570 
    571 
    572 /*
    573  * called before exit to wait for all threads to finish
    574  */
    575 static void
    576 clean_up(void)
    577 {
    578    int i;
    579 
    580    /* wait for threads to finish */
    581    for (i = 0; i < NumWinThreads; i++) {
    582       pthread_join(WinThreads[i].Thread, NULL);
    583    }
    584 
    585    for (i = 0; i < NumWinThreads; i++) {
    586       eglDestroyContext(WinThreads[i].Display, WinThreads[i].Context);
    587       XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
    588    }
    589 }
    590 
    591 
    592 static void
    593 usage(void)
    594 {
    595    printf("xeglthreads: test of EGL/GL thread safety (any key = exit)\n");
    596    printf("Usage:\n");
    597    printf("  xeglthreads [options]\n");
    598    printf("Options:\n");
    599    printf("   -display DISPLAYNAME  Specify display string\n");
    600    printf("   -n NUMTHREADS  Number of threads to create\n");
    601    printf("   -p  Use a separate display connection for each thread\n");
    602    printf("   -l  Use application-side locking\n");
    603    printf("   -t  Enable texturing\n");
    604    printf("Keyboard:\n");
    605    printf("   Esc  Exit\n");
    606    printf("   t    Change texture image (requires -t option)\n");
    607    printf("   a    Toggle animation\n");
    608    printf("   s    Step rotation (when not animating)\n");
    609 }
    610 
    611 
    612 int
    613 main(int argc, char *argv[])
    614 {
    615    char *displayName = NULL;
    616    int numThreads = 2;
    617    Display *dpy = NULL;
    618    EGLDisplay *egl_dpy = NULL;
    619    int i;
    620    Status threadStat;
    621 
    622    if (argc == 1) {
    623       usage();
    624    }
    625    else {
    626       int i;
    627       for (i = 1; i < argc; i++) {
    628          if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
    629             displayName = argv[i + 1];
    630             i++;
    631          }
    632          else if (strcmp(argv[i], "-p") == 0) {
    633             MultiDisplays = 1;
    634          }
    635          else if (strcmp(argv[i], "-l") == 0) {
    636             Locking = 1;
    637          }
    638          else if (strcmp(argv[i], "-t") == 0) {
    639             Texture = 1;
    640          }
    641          else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
    642             numThreads = atoi(argv[i + 1]);
    643             if (numThreads < 1)
    644                numThreads = 1;
    645             else if (numThreads > MAX_WINTHREADS)
    646                numThreads = MAX_WINTHREADS;
    647             i++;
    648          }
    649          else {
    650             usage();
    651             exit(1);
    652          }
    653       }
    654    }
    655 
    656    if (Locking)
    657       printf("xeglthreads: Using explicit locks around Xlib calls.\n");
    658    else
    659       printf("xeglthreads: No explict locking.\n");
    660 
    661    if (MultiDisplays)
    662       printf("xeglthreads: Per-thread display connections.\n");
    663    else
    664       printf("xeglthreads: Single display connection.\n");
    665 
    666    /*
    667     * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
    668     */
    669    if (!MultiDisplays) {
    670        if (!Locking) {
    671            threadStat = XInitThreads();
    672            if (threadStat) {
    673                printf("XInitThreads() returned %d (success)\n",
    674                       (int) threadStat);
    675            }
    676            else {
    677                printf("XInitThreads() returned 0 "
    678                       "(failure- this program may fail)\n");
    679            }
    680        }
    681 
    682       dpy = XOpenDisplay(displayName);
    683       if (!dpy) {
    684          fprintf(stderr, "Unable to open display %s\n",
    685                  XDisplayName(displayName));
    686          return -1;
    687       }
    688       egl_dpy = eglGetDisplay(dpy);
    689       if (!egl_dpy) {
    690          fprintf(stderr, "Unable to get EGL display\n");
    691          XCloseDisplay(dpy);
    692          return -1;
    693       }
    694       if (!eglInitialize(egl_dpy, NULL, NULL)) {
    695           fprintf(stderr, "Unable to initialize EGL display\n");
    696           return -1;
    697       }
    698    }
    699 
    700    pthread_mutex_init(&Mutex, NULL);
    701    pthread_mutex_init(&CondMutex, NULL);
    702    pthread_cond_init(&CondVar, NULL);
    703 
    704    printf("xeglthreads: creating windows\n");
    705 
    706    NumWinThreads = numThreads;
    707 
    708    /* Create the EGL windows and contexts */
    709    for (i = 0; i < numThreads; i++) {
    710       EGLContext share;
    711 
    712       if (MultiDisplays) {
    713          WinThreads[i].Dpy = XOpenDisplay(displayName);
    714          assert(WinThreads[i].Dpy);
    715          WinThreads[i].Display = eglGetDisplay(WinThreads[i].Dpy);
    716          assert(eglInitialize(WinThreads[i].Display, NULL, NULL));
    717       }
    718       else {
    719          WinThreads[i].Dpy = dpy;
    720          WinThreads[i].Display = egl_dpy;
    721       }
    722       WinThreads[i].Index = i;
    723       WinThreads[i].Initialized = GL_FALSE;
    724 
    725       share = (Texture && i > 0) ? WinThreads[0].Context : 0;
    726 
    727       create_window(&WinThreads[i], share);
    728    }
    729 
    730    printf("xeglthreads: creating threads\n");
    731 
    732    /* Create the threads */
    733    for (i = 0; i < numThreads; i++) {
    734       pthread_create(&WinThreads[i].Thread, NULL, thread_function,
    735                      (void*) &WinThreads[i]);
    736       printf("xeglthreads: Created thread %p\n",
    737               (void *) WinThreads[i].Thread);
    738    }
    739 
    740    if (MultiDisplays)
    741       event_loop_multi();
    742    else
    743       event_loop(dpy);
    744 
    745    clean_up();
    746 
    747    if (MultiDisplays) {
    748       for (i = 0; i < numThreads; i++) {
    749           eglTerminate(WinThreads[i].Display);
    750           XCloseDisplay(WinThreads[i].Dpy);
    751       }
    752    }
    753    else {
    754       eglTerminate(egl_dpy);
    755       XCloseDisplay(dpy);
    756    }
    757 
    758    return 0;
    759 }
    760 
    761 
    762 #else /* PTHREADS */
    763 
    764 
    765 #include <stdio.h>
    766 
    767 int
    768 main(int argc, char *argv[])
    769 {
    770    printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
    771    return 0;
    772 }
    773 
    774 
    775 #endif /* PTHREADS */
    776