2
* Copyright (C) 2000 Brian Paul All Rights Reserved.
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:
11
* The above copyright notice and this permission notice shall be included
12
* in all copies or substantial portions of the Software.
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.
21
* Ported to EGL by Chia-I Wu <olvaffe@gmail.com>
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
34
* Brian Paul 20 July 2000
40
* - Each thread gets its own EGL context.
42
* - The EGL contexts share texture objects.
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.
50
#if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
54
#include <X11/Xutil.h>
66
* Each window/thread/context:
77
int WinWidth, WinHeight;
79
GLboolean Initialized;
80
GLboolean MakeNewTexture;
84
#define MAX_WINTHREADS 100
85
static struct winthread WinThreads[MAX_WINTHREADS];
86
static int NumWinThreads = 0;
87
static volatile GLboolean ExitFlag = GL_FALSE;
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;
95
static pthread_mutex_t Mutex;
96
static pthread_cond_t CondVar;
97
static pthread_mutex_t CondMutex;
101
Error(const char *msg)
103
fprintf(stderr, "Error: %s\n", msg);
111
pthread_mutex_lock(&CondMutex);
112
pthread_cond_broadcast(&CondVar);
113
pthread_mutex_unlock(&CondMutex);
118
MakeNewTexture(struct winthread *wt)
121
static float step = 0.0;
122
GLfloat image[TEX_SIZE][TEX_SIZE][4];
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;
133
image[j][i][2] = 0.75 + 0.25 * cos(r);
134
image[j][i][3] = 1.0;
140
glBindTexture(GL_TEXTURE_2D, TexObj);
142
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &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);
151
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
152
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
154
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
155
GL_RGBA, GL_FLOAT, image);
161
/* draw a colored cube */
166
glScalef(0.75, 0.75, 0.75);
171
glBindTexture(GL_TEXTURE_2D, TexObj);
172
glEnable(GL_TEXTURE_2D);
175
glDisable(GL_TEXTURE_2D);
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);
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);
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);
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);
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);
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);
228
/* signal resize of given window */
230
resize(struct winthread *wt, int w, int h)
232
wt->NewSize = GL_TRUE;
241
* We have an instance of this for each thread.
244
draw_loop(struct winthread *wt)
249
pthread_mutex_lock(&Mutex);
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*/) {
258
wt->Initialized = GL_TRUE;
262
pthread_mutex_unlock(&Mutex);
264
eglBindAPI(EGL_OPENGL_API);
265
if (eglGetCurrentContext() != wt->Context) {
266
printf("xeglthreads: current context %p != %p\n",
267
eglGetCurrentContext(), wt->Context);
270
glEnable(GL_DEPTH_TEST);
273
GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
274
glViewport(0, 0, wt->WinWidth, wt->WinHeight);
275
glMatrixMode(GL_PROJECTION);
277
glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
278
glMatrixMode(GL_MODELVIEW);
280
glTranslatef(0, 0, -2.5);
281
wt->NewSize = GL_FALSE;
284
if (wt->MakeNewTexture) {
286
wt->MakeNewTexture = GL_FALSE;
289
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
292
glRotatef(wt->Angle, 0, 1, 0);
293
glRotatef(wt->Angle, 1, 0, 0);
294
glScalef(0.7, 0.7, 0.7);
299
pthread_mutex_lock(&Mutex);
301
eglSwapBuffers(wt->Display, wt->Surface);
304
pthread_mutex_unlock(&Mutex);
310
/* wait for signal to draw */
311
pthread_mutex_lock(&CondMutex);
312
pthread_cond_wait(&CondVar, &CondMutex);
313
pthread_mutex_unlock(&CondMutex);
317
eglMakeCurrent(wt->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
322
keypress(XEvent *event, struct winthread *wt)
328
XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat);
332
/* tell all threads to exit */
337
/*printf("exit draw_loop %d\n", wt->Index);*/
342
wt->MakeNewTexture = GL_TRUE;
350
if (Animate) /* yes, prev Animate state! */
365
* The main process thread runs this loop.
366
* Single display connection for all threads.
369
event_loop(Display *dpy)
374
assert(!MultiDisplays);
381
pthread_mutex_lock(&Mutex);
384
XNextEvent(dpy, &event);
385
pthread_mutex_unlock(&Mutex);
388
pthread_mutex_unlock(&Mutex);
393
XNextEvent(dpy, &event);
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);
409
for (i = 0; i < NumWinThreads; i++) {
410
struct winthread *wt = &WinThreads[i];
411
if (event.xkey.window == wt->Win) {
412
keypress(&event, wt);
425
* Separate display connection for each thread.
428
event_loop_multi(void)
433
assert(MultiDisplays);
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);
444
keypress(&event, wt);
450
w = (w + 1) % NumWinThreads;
458
* we'll call this once for each thread, before the threads are created.
461
create_window(struct winthread *wt, EGLContext shareCtx)
466
EGLint attribs[] = { EGL_RED_SIZE, 1,
470
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
476
XSetWindowAttributes attr;
479
XVisualInfo *visinfo, visTemplate;
481
int width = 160, height = 160;
482
int xpos = (wt->Index % 8) * (width + 10);
483
int ypos = (wt->Index / 8) * (width + 20);
485
scrnum = DefaultScreen(wt->Dpy);
486
root = RootWindow(wt->Dpy, scrnum);
488
if (!eglChooseConfig(wt->Display, attribs, &config, 1, &num_configs) ||
490
Error("Unable to choose an EGL config");
494
assert(num_configs > 0);
496
if (!eglGetConfigAttrib(wt->Display, config, EGL_NATIVE_VISUAL_ID, &vid)) {
497
Error("Unable to get visual id of EGL config\n");
500
visTemplate.visualid = vid;
501
visinfo = XGetVisualInfo(wt->Dpy, VisualIDMask,
502
&visTemplate, &num_visuals);
504
Error("Unable to find RGB, Z, double-buffered visual");
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;
514
win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
515
0, visinfo->depth, InputOutput,
516
visinfo->visual, mask, &attr);
518
Error("Couldn't create window");
524
XSizeHints sizehints;
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);
535
eglBindAPI(EGL_OPENGL_API);
537
ctx = eglCreateContext(wt->Display, config, shareCtx, NULL);
539
Error("Couldn't create EGL context");
541
surf = eglCreateWindowSurface(wt->Display, config, win, NULL);
543
Error("Couldn't create EGL surface");
546
XMapWindow(wt->Dpy, win);
549
/* save the info for this window/context */
554
wt->WinWidth = width;
555
wt->WinHeight = height;
556
wt->NewSize = GL_TRUE;
561
* Called by pthread_create()
564
thread_function(void *p)
566
struct winthread *wt = (struct winthread *) p;
573
* called before exit to wait for all threads to finish
580
/* wait for threads to finish */
581
for (i = 0; i < NumWinThreads; i++) {
582
pthread_join(WinThreads[i].Thread, NULL);
585
for (i = 0; i < NumWinThreads; i++) {
586
eglDestroyContext(WinThreads[i].Display, WinThreads[i].Context);
587
XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
595
printf("xeglthreads: test of EGL/GL thread safety (any key = exit)\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");
613
main(int argc, char *argv[])
615
char *displayName = NULL;
618
EGLDisplay *egl_dpy = NULL;
627
for (i = 1; i < argc; i++) {
628
if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
629
displayName = argv[i + 1];
632
else if (strcmp(argv[i], "-p") == 0) {
635
else if (strcmp(argv[i], "-l") == 0) {
638
else if (strcmp(argv[i], "-t") == 0) {
641
else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
642
numThreads = atoi(argv[i + 1]);
645
else if (numThreads > MAX_WINTHREADS)
646
numThreads = MAX_WINTHREADS;
657
printf("xeglthreads: Using explicit locks around Xlib calls.\n");
659
printf("xeglthreads: No explict locking.\n");
662
printf("xeglthreads: Per-thread display connections.\n");
664
printf("xeglthreads: Single display connection.\n");
667
* VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
669
if (!MultiDisplays) {
671
threadStat = XInitThreads();
673
printf("XInitThreads() returned %d (success)\n",
677
printf("XInitThreads() returned 0 "
678
"(failure- this program may fail)\n");
682
dpy = XOpenDisplay(displayName);
684
fprintf(stderr, "Unable to open display %s\n",
685
XDisplayName(displayName));
688
egl_dpy = eglGetDisplay(dpy);
690
fprintf(stderr, "Unable to get EGL display\n");
694
if (!eglInitialize(egl_dpy, NULL, NULL)) {
695
fprintf(stderr, "Unable to initialize EGL display\n");
700
pthread_mutex_init(&Mutex, NULL);
701
pthread_mutex_init(&CondMutex, NULL);
702
pthread_cond_init(&CondVar, NULL);
704
printf("xeglthreads: creating windows\n");
706
NumWinThreads = numThreads;
708
/* Create the EGL windows and contexts */
709
for (i = 0; i < numThreads; i++) {
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));
719
WinThreads[i].Dpy = dpy;
720
WinThreads[i].Display = egl_dpy;
722
WinThreads[i].Index = i;
723
WinThreads[i].Initialized = GL_FALSE;
725
share = (Texture && i > 0) ? WinThreads[0].Context : 0;
727
create_window(&WinThreads[i], share);
730
printf("xeglthreads: creating threads\n");
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);
748
for (i = 0; i < numThreads; i++) {
749
eglTerminate(WinThreads[i].Display);
750
XCloseDisplay(WinThreads[i].Dpy);
754
eglTerminate(egl_dpy);
768
main(int argc, char *argv[])
770
printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
775
#endif /* PTHREADS */