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.
24 * This program tests GLX thread safety.
25 * Command line options:
26 * -p Open a display connection for each thread
27 * -l Enable application-side locking
28 * -n <num threads> Number of threads to create (default is 2)
29 * -display <display name> Specify X display (default is $DISPLAY)
30 * -t Use texture mapping
32 * Brian Paul 20 July 2000
38 * - Each thread gets its own GLX context.
40 * - The GLX contexts share texture objects.
42 * - When 't' is pressed to update the texture image, the window/thread which
43 * has input focus is signalled to change the texture. The other threads
44 * should see the updated texture the next time they call glBindTexture.
48 #if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
62 * Each window/thread/context:
71 int WinWidth
, WinHeight
;
73 GLboolean Initialized
;
74 GLboolean MakeNewTexture
;
78 #define MAX_WINTHREADS 100
79 static struct winthread WinThreads
[MAX_WINTHREADS
];
80 static int NumWinThreads
= 0;
81 static volatile GLboolean ExitFlag
= GL_FALSE
;
83 static GLboolean MultiDisplays
= 0;
84 static GLboolean Locking
= 0;
85 static GLboolean Texture
= GL_FALSE
;
86 static GLuint TexObj
= 12;
87 static GLboolean Animate
= GL_TRUE
;
89 static pthread_mutex_t Mutex
;
90 static pthread_cond_t CondVar
;
91 static pthread_mutex_t CondMutex
;
95 Error(const char *msg
)
97 fprintf(stderr
, "Error: %s\n", msg
);
105 pthread_mutex_lock(&CondMutex
);
106 pthread_cond_broadcast(&CondVar
);
107 pthread_mutex_unlock(&CondMutex
);
112 MakeNewTexture(struct winthread
*wt
)
115 static float step
= 0.0;
116 GLfloat image
[TEX_SIZE
][TEX_SIZE
][4];
120 for (j
= 0; j
< TEX_SIZE
; j
++) {
121 for (i
= 0; i
< TEX_SIZE
; i
++) {
122 float dt
= 5.0 * (j
- 0.5 * TEX_SIZE
) / TEX_SIZE
;
123 float ds
= 5.0 * (i
- 0.5 * TEX_SIZE
) / TEX_SIZE
;
124 float r
= dt
* dt
+ ds
* ds
+ step
;
127 image
[j
][i
][2] = 0.75 + 0.25 * cos(r
);
128 image
[j
][i
][3] = 1.0;
134 glBindTexture(GL_TEXTURE_2D
, TexObj
);
136 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_WIDTH
, &width
);
138 assert(width
== TEX_SIZE
);
139 /* sub-tex replace */
140 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, TEX_SIZE
, TEX_SIZE
,
141 GL_RGBA
, GL_FLOAT
, image
);
145 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
146 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
148 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, TEX_SIZE
, TEX_SIZE
, 0,
149 GL_RGBA
, GL_FLOAT
, image
);
155 /* draw a colored cube */
160 glScalef(0.75, 0.75, 0.75);
165 glBindTexture(GL_TEXTURE_2D
, TexObj
);
166 glEnable(GL_TEXTURE_2D
);
169 glDisable(GL_TEXTURE_2D
);
176 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
177 glTexCoord2f(1, 0); glVertex3f(-1, 1, -1);
178 glTexCoord2f(1, 1); glVertex3f(-1, 1, 1);
179 glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
183 glTexCoord2f(0, 0); glVertex3f(1, -1, -1);
184 glTexCoord2f(1, 0); glVertex3f(1, 1, -1);
185 glTexCoord2f(1, 1); glVertex3f(1, 1, 1);
186 glTexCoord2f(0, 1); glVertex3f(1, -1, 1);
190 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
191 glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
192 glTexCoord2f(1, 1); glVertex3f( 1, -1, 1);
193 glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
197 glTexCoord2f(0, 0); glVertex3f(-1, 1, -1);
198 glTexCoord2f(1, 0); glVertex3f( 1, 1, -1);
199 glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
200 glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
204 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
205 glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
206 glTexCoord2f(1, 1); glVertex3f( 1, 1, -1);
207 glTexCoord2f(0, 1); glVertex3f(-1, 1, -1);
211 glTexCoord2f(0, 0); glVertex3f(-1, -1, 1);
212 glTexCoord2f(1, 0); glVertex3f( 1, -1, 1);
213 glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
214 glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
222 /* signal resize of given window */
224 resize(struct winthread
*wt
, int w
, int h
)
226 wt
->NewSize
= GL_TRUE
;
235 * We have an instance of this for each thread.
238 draw_loop(struct winthread
*wt
)
243 pthread_mutex_lock(&Mutex
);
245 glXMakeCurrent(wt
->Dpy
, wt
->Win
, wt
->Context
);
246 if (!wt
->Initialized
) {
247 printf("glthreads: %d: GL_RENDERER = %s\n", wt
->Index
,
248 (char *) glGetString(GL_RENDERER
));
249 if (Texture
/*&& wt->Index == 0*/) {
252 wt
->Initialized
= GL_TRUE
;
256 pthread_mutex_unlock(&Mutex
);
258 glEnable(GL_DEPTH_TEST
);
261 GLfloat w
= (float) wt
->WinWidth
/ (float) wt
->WinHeight
;
262 glViewport(0, 0, wt
->WinWidth
, wt
->WinHeight
);
263 glMatrixMode(GL_PROJECTION
);
265 glFrustum(-w
, w
, -1.0, 1.0, 1.5, 10);
266 glMatrixMode(GL_MODELVIEW
);
268 glTranslatef(0, 0, -2.5);
269 wt
->NewSize
= GL_FALSE
;
272 if (wt
->MakeNewTexture
) {
274 wt
->MakeNewTexture
= GL_FALSE
;
277 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
280 glRotatef(wt
->Angle
, 0, 1, 0);
281 glRotatef(wt
->Angle
, 1, 0, 0);
282 glScalef(0.7, 0.7, 0.7);
287 pthread_mutex_lock(&Mutex
);
289 glXSwapBuffers(wt
->Dpy
, wt
->Win
);
292 pthread_mutex_unlock(&Mutex
);
298 /* wait for signal to draw */
299 pthread_mutex_lock(&CondMutex
);
300 pthread_cond_wait(&CondVar
, &CondMutex
);
301 pthread_mutex_unlock(&CondMutex
);
309 keypress(XEvent
*event
, struct winthread
*wt
)
315 XLookupString(&event
->xkey
, buf
, sizeof(buf
), &keySym
, &stat
);
319 /* tell all threads to exit */
324 /*printf("exit draw_loop %d\n", wt->Index);*/
329 wt
->MakeNewTexture
= GL_TRUE
;
337 if (Animate
) /* yes, prev Animate state! */
352 * The main process thread runs this loop.
353 * Single display connection for all threads.
356 event_loop(Display
*dpy
)
361 assert(!MultiDisplays
);
368 pthread_mutex_lock(&Mutex
);
371 XNextEvent(dpy
, &event
);
372 pthread_mutex_unlock(&Mutex
);
375 pthread_mutex_unlock(&Mutex
);
380 XNextEvent(dpy
, &event
);
383 switch (event
.type
) {
384 case ConfigureNotify
:
385 /* Find winthread for this event's window */
386 for (i
= 0; i
< NumWinThreads
; i
++) {
387 struct winthread
*wt
= &WinThreads
[i
];
388 if (event
.xconfigure
.window
== wt
->Win
) {
389 resize(wt
, event
.xconfigure
.width
,
390 event
.xconfigure
.height
);
396 for (i
= 0; i
< NumWinThreads
; i
++) {
397 struct winthread
*wt
= &WinThreads
[i
];
398 if (event
.xkey
.window
== wt
->Win
) {
399 keypress(&event
, wt
);
412 * Separate display connection for each thread.
415 event_loop_multi(void)
420 assert(MultiDisplays
);
423 struct winthread
*wt
= &WinThreads
[w
];
424 if (XPending(wt
->Dpy
)) {
425 XNextEvent(wt
->Dpy
, &event
);
426 switch (event
.type
) {
427 case ConfigureNotify
:
428 resize(wt
, event
.xconfigure
.width
, event
.xconfigure
.height
);
431 keypress(&event
, wt
);
437 w
= (w
+ 1) % NumWinThreads
;
445 * we'll call this once for each thread, before the threads are created.
448 create_window(struct winthread
*wt
, GLXContext shareCtx
)
452 int attrib
[] = { GLX_RGBA
,
460 XSetWindowAttributes attr
;
463 XVisualInfo
*visinfo
;
464 int width
= 160, height
= 160;
465 int xpos
= (wt
->Index
% 8) * (width
+ 10);
466 int ypos
= (wt
->Index
/ 8) * (width
+ 20);
468 scrnum
= DefaultScreen(wt
->Dpy
);
469 root
= RootWindow(wt
->Dpy
, scrnum
);
471 visinfo
= glXChooseVisual(wt
->Dpy
, scrnum
, attrib
);
473 Error("Unable to find RGB, Z, double-buffered visual");
476 /* window attributes */
477 attr
.background_pixel
= 0;
478 attr
.border_pixel
= 0;
479 attr
.colormap
= XCreateColormap(wt
->Dpy
, root
, visinfo
->visual
, AllocNone
);
480 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
481 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
483 win
= XCreateWindow(wt
->Dpy
, root
, xpos
, ypos
, width
, height
,
484 0, visinfo
->depth
, InputOutput
,
485 visinfo
->visual
, mask
, &attr
);
487 Error("Couldn't create window");
491 XSizeHints sizehints
;
494 sizehints
.width
= width
;
495 sizehints
.height
= height
;
496 sizehints
.flags
= USSize
| USPosition
;
497 XSetNormalHints(wt
->Dpy
, win
, &sizehints
);
498 XSetStandardProperties(wt
->Dpy
, win
, "glthreads", "glthreads",
499 None
, (char **)NULL
, 0, &sizehints
);
503 ctx
= glXCreateContext(wt
->Dpy
, visinfo
, shareCtx
, True
);
505 Error("Couldn't create GLX context");
508 XMapWindow(wt
->Dpy
, win
);
511 /* save the info for this window/context */
515 wt
->WinWidth
= width
;
516 wt
->WinHeight
= height
;
517 wt
->NewSize
= GL_TRUE
;
522 * Called by pthread_create()
525 thread_function(void *p
)
527 struct winthread
*wt
= (struct winthread
*) p
;
534 * called before exit to wait for all threads to finish
541 /* wait for threads to finish */
542 for (i
= 0; i
< NumWinThreads
; i
++) {
543 pthread_join(WinThreads
[i
].Thread
, NULL
);
546 for (i
= 0; i
< NumWinThreads
; i
++) {
547 glXDestroyContext(WinThreads
[i
].Dpy
, WinThreads
[i
].Context
);
548 XDestroyWindow(WinThreads
[i
].Dpy
, WinThreads
[i
].Win
);
556 printf("glthreads: test of GL thread safety (any key = exit)\n");
558 printf(" glthreads [options]\n");
559 printf("Options:\n");
560 printf(" -display DISPLAYNAME Specify display string\n");
561 printf(" -n NUMTHREADS Number of threads to create\n");
562 printf(" -p Use a separate display connection for each thread\n");
563 printf(" -l Use application-side locking\n");
564 printf(" -t Enable texturing\n");
565 printf("Keyboard:\n");
566 printf(" Esc Exit\n");
567 printf(" t Change texture image (requires -t option)\n");
568 printf(" a Toggle animation\n");
569 printf(" s Step rotation (when not animating)\n");
574 main(int argc
, char *argv
[])
576 char *displayName
= NULL
;
587 for (i
= 1; i
< argc
; i
++) {
588 if (strcmp(argv
[i
], "-display") == 0 && i
+ 1 < argc
) {
589 displayName
= argv
[i
+ 1];
592 else if (strcmp(argv
[i
], "-p") == 0) {
595 else if (strcmp(argv
[i
], "-l") == 0) {
598 else if (strcmp(argv
[i
], "-t") == 0) {
601 else if (strcmp(argv
[i
], "-n") == 0 && i
+ 1 < argc
) {
602 numThreads
= atoi(argv
[i
+ 1]);
605 else if (numThreads
> MAX_WINTHREADS
)
606 numThreads
= MAX_WINTHREADS
;
617 printf("glthreads: Using explicit locks around Xlib calls.\n");
619 printf("glthreads: No explict locking.\n");
622 printf("glthreads: Per-thread display connections.\n");
624 printf("glthreads: Single display connection.\n");
627 * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
629 if (!MultiDisplays
) {
631 threadStat
= XInitThreads();
633 printf("XInitThreads() returned %d (success)\n", (int) threadStat
);
636 printf("XInitThreads() returned 0 (failure- this program may fail)\n");
640 dpy
= XOpenDisplay(displayName
);
642 fprintf(stderr
, "Unable to open display %s\n", XDisplayName(displayName
));
647 pthread_mutex_init(&Mutex
, NULL
);
648 pthread_mutex_init(&CondMutex
, NULL
);
649 pthread_cond_init(&CondVar
, NULL
);
651 printf("glthreads: creating windows\n");
653 NumWinThreads
= numThreads
;
655 /* Create the GLX windows and contexts */
656 for (i
= 0; i
< numThreads
; i
++) {
660 WinThreads
[i
].Dpy
= XOpenDisplay(displayName
);
661 assert(WinThreads
[i
].Dpy
);
664 WinThreads
[i
].Dpy
= dpy
;
666 WinThreads
[i
].Index
= i
;
667 WinThreads
[i
].Initialized
= GL_FALSE
;
669 share
= (Texture
&& i
> 0) ? WinThreads
[0].Context
: 0;
671 create_window(&WinThreads
[i
], share
);
674 printf("glthreads: creating threads\n");
676 /* Create the threads */
677 for (i
= 0; i
< numThreads
; i
++) {
678 pthread_create(&WinThreads
[i
].Thread
, NULL
, thread_function
,
679 (void*) &WinThreads
[i
]);
680 printf("glthreads: Created thread %p\n", (void *) WinThreads
[i
].Thread
);
691 for (i
= 0; i
< numThreads
; i
++) {
692 XCloseDisplay(WinThreads
[i
].Dpy
);
709 main(int argc
, char *argv
[])
711 printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
716 #endif /* PTHREADS */