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 */