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,
475 XSetWindowAttributes attr
;
478 XVisualInfo
*visinfo
, visTemplate
;
480 int width
= 160, height
= 160;
481 int xpos
= (wt
->Index
% 8) * (width
+ 10);
482 int ypos
= (wt
->Index
/ 8) * (width
+ 20);
484 scrnum
= DefaultScreen(wt
->Dpy
);
485 root
= RootWindow(wt
->Dpy
, scrnum
);
487 if (!eglChooseConfig(wt
->Display
, attribs
, &config
, 1, &num_configs
)) {
488 Error("Unable to choose an EGL config");
492 assert(num_configs
> 0);
494 if (!eglGetConfigAttrib(wt
->Display
, config
, EGL_NATIVE_VISUAL_ID
, &vid
)) {
495 Error("Unable to get visual id of EGL config\n");
498 visTemplate
.visualid
= vid
;
499 visinfo
= XGetVisualInfo(wt
->Dpy
, VisualIDMask
,
500 &visTemplate
, &num_visuals
);
502 Error("Unable to find RGB, Z, double-buffered visual");
505 /* window attributes */
506 attr
.background_pixel
= 0;
507 attr
.border_pixel
= 0;
508 attr
.colormap
= XCreateColormap(wt
->Dpy
, root
, visinfo
->visual
, AllocNone
);
509 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
510 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
512 win
= XCreateWindow(wt
->Dpy
, root
, xpos
, ypos
, width
, height
,
513 0, visinfo
->depth
, InputOutput
,
514 visinfo
->visual
, mask
, &attr
);
516 Error("Couldn't create window");
522 XSizeHints sizehints
;
525 sizehints
.width
= width
;
526 sizehints
.height
= height
;
527 sizehints
.flags
= USSize
| USPosition
;
528 XSetNormalHints(wt
->Dpy
, win
, &sizehints
);
529 XSetStandardProperties(wt
->Dpy
, win
, "xeglthreads", "xeglthreads",
530 None
, (char **)NULL
, 0, &sizehints
);
533 eglBindAPI(EGL_OPENGL_API
);
535 ctx
= eglCreateContext(wt
->Display
, config
, shareCtx
, NULL
);
537 Error("Couldn't create EGL context");
539 surf
= eglCreateWindowSurface(wt
->Display
, config
, win
, NULL
);
541 Error("Couldn't create EGL surface");
544 XMapWindow(wt
->Dpy
, win
);
547 /* save the info for this window/context */
552 wt
->WinWidth
= width
;
553 wt
->WinHeight
= height
;
554 wt
->NewSize
= GL_TRUE
;
559 * Called by pthread_create()
562 thread_function(void *p
)
564 struct winthread
*wt
= (struct winthread
*) p
;
571 * called before exit to wait for all threads to finish
578 /* wait for threads to finish */
579 for (i
= 0; i
< NumWinThreads
; i
++) {
580 pthread_join(WinThreads
[i
].Thread
, NULL
);
583 for (i
= 0; i
< NumWinThreads
; i
++) {
584 eglDestroyContext(WinThreads
[i
].Display
, WinThreads
[i
].Context
);
585 XDestroyWindow(WinThreads
[i
].Dpy
, WinThreads
[i
].Win
);
593 printf("xeglthreads: test of EGL/GL thread safety (any key = exit)\n");
595 printf(" xeglthreads [options]\n");
596 printf("Options:\n");
597 printf(" -display DISPLAYNAME Specify display string\n");
598 printf(" -n NUMTHREADS Number of threads to create\n");
599 printf(" -p Use a separate display connection for each thread\n");
600 printf(" -l Use application-side locking\n");
601 printf(" -t Enable texturing\n");
602 printf("Keyboard:\n");
603 printf(" Esc Exit\n");
604 printf(" t Change texture image (requires -t option)\n");
605 printf(" a Toggle animation\n");
606 printf(" s Step rotation (when not animating)\n");
611 main(int argc
, char *argv
[])
613 char *displayName
= NULL
;
616 EGLDisplay
*egl_dpy
= NULL
;
625 for (i
= 1; i
< argc
; i
++) {
626 if (strcmp(argv
[i
], "-display") == 0 && i
+ 1 < argc
) {
627 displayName
= argv
[i
+ 1];
630 else if (strcmp(argv
[i
], "-p") == 0) {
633 else if (strcmp(argv
[i
], "-l") == 0) {
636 else if (strcmp(argv
[i
], "-t") == 0) {
639 else if (strcmp(argv
[i
], "-n") == 0 && i
+ 1 < argc
) {
640 numThreads
= atoi(argv
[i
+ 1]);
643 else if (numThreads
> MAX_WINTHREADS
)
644 numThreads
= MAX_WINTHREADS
;
655 printf("xeglthreads: Using explicit locks around Xlib calls.\n");
657 printf("xeglthreads: No explict locking.\n");
660 printf("xeglthreads: Per-thread display connections.\n");
662 printf("xeglthreads: Single display connection.\n");
665 * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
667 if (!MultiDisplays
) {
669 threadStat
= XInitThreads();
671 printf("XInitThreads() returned %d (success)\n",
675 printf("XInitThreads() returned 0 "
676 "(failure- this program may fail)\n");
680 dpy
= XOpenDisplay(displayName
);
682 fprintf(stderr
, "Unable to open display %s\n",
683 XDisplayName(displayName
));
686 egl_dpy
= eglGetDisplay(dpy
);
688 fprintf(stderr
, "Unable to get EGL display\n");
692 if (!eglInitialize(egl_dpy
, NULL
, NULL
)) {
693 fprintf(stderr
, "Unable to initialize EGL display\n");
698 pthread_mutex_init(&Mutex
, NULL
);
699 pthread_mutex_init(&CondMutex
, NULL
);
700 pthread_cond_init(&CondVar
, NULL
);
702 printf("xeglthreads: creating windows\n");
704 NumWinThreads
= numThreads
;
706 /* Create the EGL windows and contexts */
707 for (i
= 0; i
< numThreads
; i
++) {
711 WinThreads
[i
].Dpy
= XOpenDisplay(displayName
);
712 assert(WinThreads
[i
].Dpy
);
713 WinThreads
[i
].Display
= eglGetDisplay(WinThreads
[i
].Dpy
);
714 assert(eglInitialize(WinThreads
[i
].Display
, NULL
, NULL
));
717 WinThreads
[i
].Dpy
= dpy
;
718 WinThreads
[i
].Display
= egl_dpy
;
720 WinThreads
[i
].Index
= i
;
721 WinThreads
[i
].Initialized
= GL_FALSE
;
723 share
= (Texture
&& i
> 0) ? WinThreads
[0].Context
: 0;
725 create_window(&WinThreads
[i
], share
);
728 printf("xeglthreads: creating threads\n");
730 /* Create the threads */
731 for (i
= 0; i
< numThreads
; i
++) {
732 pthread_create(&WinThreads
[i
].Thread
, NULL
, thread_function
,
733 (void*) &WinThreads
[i
]);
734 printf("xeglthreads: Created thread %p\n",
735 (void *) WinThreads
[i
].Thread
);
746 for (i
= 0; i
< numThreads
; i
++) {
747 eglTerminate(WinThreads
[i
].Display
);
748 XCloseDisplay(WinThreads
[i
].Dpy
);
752 eglTerminate(egl_dpy
);
766 main(int argc
, char *argv
[])
768 printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
773 #endif /* PTHREADS */