blob: 4f87ee75d40636223094d66d2bf30782c76b0bd9 [file] [log] [blame]
Brian Paulc7d14442000-07-20 20:12:17 +00001/*
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
22
23/*
24 * This program tests GLX thread safety.
25 * Command line options:
Brian Paul436d72e2006-03-23 16:58:22 +000026 * -p Open a display connection for each thread
Brian Paul12a317b2008-05-07 09:14:10 -060027 * -l Enable application-side locking
Brian Paulc7d14442000-07-20 20:12:17 +000028 * -n <num threads> Number of threads to create (default is 2)
29 * -display <display name> Specify X display (default is :0.0)
30 *
31 * Brian Paul 20 July 2000
32 */
33
34
35#if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
36
Brian Paul436d72e2006-03-23 16:58:22 +000037#include <assert.h>
Brian Paulc7d14442000-07-20 20:12:17 +000038#include <GL/gl.h>
39#include <GL/glx.h>
40#include <stdio.h>
41#include <stdlib.h>
Brian Paula3e44f42002-03-08 19:44:28 +000042#include <string.h>
Brian Paulc7d14442000-07-20 20:12:17 +000043#include <unistd.h>
44#include <pthread.h>
45
46
47/*
48 * Each window/thread/context:
49 */
50struct winthread {
51 Display *Dpy;
52 int Index;
53 pthread_t Thread;
54 Window Win;
55 GLXContext Context;
56 float Angle;
57 int WinWidth, WinHeight;
58 GLboolean NewSize;
59};
60
61
62#define MAX_WINTHREADS 100
63static struct winthread WinThreads[MAX_WINTHREADS];
64static int NumWinThreads = 0;
Brian Pauld07859e2004-07-02 14:35:05 +000065static volatile GLboolean ExitFlag = GL_FALSE;
Brian Paulc7d14442000-07-20 20:12:17 +000066
Brian Paul436d72e2006-03-23 16:58:22 +000067static GLboolean MultiDisplays = 0;
Brian Paulc4f27102006-03-23 17:17:23 +000068static GLboolean Locking = 0;
69
70static pthread_mutex_t Mutex;
Brian Paulc7d14442000-07-20 20:12:17 +000071
72
73static void
74Error(const char *msg)
75{
76 fprintf(stderr, "Error: %s\n", msg);
77 exit(1);
78}
79
80
81/* draw a colored cube */
82static void
83draw_object(void)
84{
85 glPushMatrix();
86 glScalef(0.75, 0.75, 0.75);
87
88 glColor3f(1, 0, 0);
89 glBegin(GL_POLYGON);
90 glVertex3f(1, -1, -1);
91 glVertex3f(1, 1, -1);
92 glVertex3f(1, 1, 1);
93 glVertex3f(1, -1, 1);
94 glEnd();
95
96 glColor3f(0, 1, 1);
97 glBegin(GL_POLYGON);
98 glVertex3f(-1, -1, -1);
99 glVertex3f(-1, 1, -1);
100 glVertex3f(-1, 1, 1);
101 glVertex3f(-1, -1, 1);
102 glEnd();
103
104 glColor3f(0, 1, 0);
105 glBegin(GL_POLYGON);
106 glVertex3f(-1, 1, -1);
107 glVertex3f( 1, 1, -1);
108 glVertex3f( 1, 1, 1);
109 glVertex3f(-1, 1, 1);
110 glEnd();
111
112 glColor3f(1, 0, 1);
113 glBegin(GL_POLYGON);
114 glVertex3f(-1, -1, -1);
115 glVertex3f( 1, -1, -1);
116 glVertex3f( 1, -1, 1);
117 glVertex3f(-1, -1, 1);
118 glEnd();
119
120 glColor3f(0, 0, 1);
121 glBegin(GL_POLYGON);
122 glVertex3f(-1, -1, 1);
123 glVertex3f( 1, -1, 1);
124 glVertex3f( 1, 1, 1);
125 glVertex3f(-1, 1, 1);
126 glEnd();
127
128 glColor3f(1, 1, 0);
129 glBegin(GL_POLYGON);
130 glVertex3f(-1, -1, -1);
131 glVertex3f( 1, -1, -1);
132 glVertex3f( 1, 1, -1);
133 glVertex3f(-1, 1, -1);
134 glEnd();
135 glPopMatrix();
136}
137
138
139/* signal resize of given window */
140static void
141resize(struct winthread *wt, int w, int h)
142{
143 wt->NewSize = GL_TRUE;
144 wt->WinWidth = w;
145 wt->WinHeight = h;
146}
147
148
149/*
150 * We have an instance of this for each thread.
151 */
152static void
153draw_loop(struct winthread *wt)
154{
Brian Paulc4f27102006-03-23 17:17:23 +0000155 GLboolean firstIter = GL_TRUE;
156
Brian Paulc7d14442000-07-20 20:12:17 +0000157 while (!ExitFlag) {
158
Brian Paulc4f27102006-03-23 17:17:23 +0000159 if (Locking)
160 pthread_mutex_lock(&Mutex);
161
Brian Paulc7d14442000-07-20 20:12:17 +0000162 glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
Brian Paulc4f27102006-03-23 17:17:23 +0000163 if (firstIter) {
164 printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index,
165 (char *) glGetString(GL_RENDERER));
166 firstIter = GL_FALSE;
167 }
168
169 if (Locking)
170 pthread_mutex_unlock(&Mutex);
Brian Paulc7d14442000-07-20 20:12:17 +0000171
172 glEnable(GL_DEPTH_TEST);
173
174 if (wt->NewSize) {
175 GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
176 glViewport(0, 0, wt->WinWidth, wt->WinHeight);
177 glMatrixMode(GL_PROJECTION);
178 glLoadIdentity();
179 glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
180 glMatrixMode(GL_MODELVIEW);
181 glLoadIdentity();
182 glTranslatef(0, 0, -2.5);
183 wt->NewSize = GL_FALSE;
184 }
185
186 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
187
188 glPushMatrix();
189 glRotatef(wt->Angle, 0, 0, 1);
190 glRotatef(wt->Angle, 1, 0, 0);
191 glScalef(0.7, 0.7, 0.7);
192 draw_object();
193 glPopMatrix();
194
Brian Paulc4f27102006-03-23 17:17:23 +0000195 if (Locking)
196 pthread_mutex_lock(&Mutex);
197
Brian Paulc7d14442000-07-20 20:12:17 +0000198 glXSwapBuffers(wt->Dpy, wt->Win);
199
Brian Paulc4f27102006-03-23 17:17:23 +0000200 if (Locking)
201 pthread_mutex_unlock(&Mutex);
202
Brian Paul436d72e2006-03-23 16:58:22 +0000203 usleep(5000);
Brian Paulc7d14442000-07-20 20:12:17 +0000204 wt->Angle += 1.0;
205 }
206}
207
208
209/*
210 * The main process thread runs this loop.
Brian Paul436d72e2006-03-23 16:58:22 +0000211 * Single display connection for all threads.
Brian Paulc7d14442000-07-20 20:12:17 +0000212 */
213static void
214event_loop(Display *dpy)
215{
Brian Pauld07859e2004-07-02 14:35:05 +0000216 XEvent event;
217 int i;
Brian Paulc7d14442000-07-20 20:12:17 +0000218
Brian Paul436d72e2006-03-23 16:58:22 +0000219 assert(!MultiDisplays);
220
Brian Pauld07859e2004-07-02 14:35:05 +0000221 while (!ExitFlag) {
Brian Paulc4f27102006-03-23 17:17:23 +0000222
223 if (Locking) {
224 while (1) {
225 int k;
226 pthread_mutex_lock(&Mutex);
227 k = XPending(dpy);
228 if (k) {
229 XNextEvent(dpy, &event);
230 pthread_mutex_unlock(&Mutex);
231 break;
232 }
233 pthread_mutex_unlock(&Mutex);
234 usleep(5000);
235 }
236 }
237 else {
238 XNextEvent(dpy, &event);
239 }
240
Brian Pauld07859e2004-07-02 14:35:05 +0000241 switch (event.type) {
242 case ConfigureNotify:
243 /* Find winthread for this event's window */
244 for (i = 0; i < NumWinThreads; i++) {
245 struct winthread *wt = &WinThreads[i];
246 if (event.xconfigure.window == wt->Win) {
247 resize(wt, event.xconfigure.width,
248 event.xconfigure.height);
249 break;
Brian Paulc7d14442000-07-20 20:12:17 +0000250 }
251 }
Brian Pauld07859e2004-07-02 14:35:05 +0000252 break;
253 case KeyPress:
254 /* tell all threads to exit */
255 ExitFlag = GL_TRUE;
256 /*printf("exit draw_loop %d\n", wt->Index);*/
257 return;
258 default:
259 /*no-op*/ ;
Brian Paulc7d14442000-07-20 20:12:17 +0000260 }
261 }
262}
263
264
265/*
Brian Paul436d72e2006-03-23 16:58:22 +0000266 * Separate display connection for each thread.
267 */
268static void
269event_loop_multi(void)
270{
271 XEvent event;
272 int w = 0;
273
274 assert(MultiDisplays);
275
276 while (!ExitFlag) {
277 struct winthread *wt = &WinThreads[w];
278 if (XPending(wt->Dpy)) {
279 XNextEvent(wt->Dpy, &event);
280 switch (event.type) {
281 case ConfigureNotify:
282 resize(wt, event.xconfigure.width, event.xconfigure.height);
283 break;
284 case KeyPress:
285 /* tell all threads to exit */
286 ExitFlag = GL_TRUE;
287 /*printf("exit draw_loop %d\n", wt->Index);*/
288 return;
289 default:
290 /*no-op*/ ;
291 }
292 }
293 w = (w + 1) % NumWinThreads;
294 usleep(5000);
295 }
296}
297
298
299
300/*
Brian Paulc7d14442000-07-20 20:12:17 +0000301 * we'll call this once for each thread, before the threads are created.
302 */
303static void
304create_window(struct winthread *wt)
305{
306 Window win;
307 GLXContext ctx;
308 int attrib[] = { GLX_RGBA,
309 GLX_RED_SIZE, 1,
310 GLX_GREEN_SIZE, 1,
311 GLX_BLUE_SIZE, 1,
312 GLX_DEPTH_SIZE, 1,
313 GLX_DOUBLEBUFFER,
314 None };
315 int scrnum;
316 XSetWindowAttributes attr;
317 unsigned long mask;
318 Window root;
319 XVisualInfo *visinfo;
320 int width = 80, height = 80;
321 int xpos = (wt->Index % 10) * 90;
322 int ypos = (wt->Index / 10) * 100;
323
324 scrnum = DefaultScreen(wt->Dpy);
325 root = RootWindow(wt->Dpy, scrnum);
326
327 visinfo = glXChooseVisual(wt->Dpy, scrnum, attrib);
328 if (!visinfo) {
329 Error("Unable to find RGB, Z, double-buffered visual");
330 }
331
332 /* window attributes */
333 attr.background_pixel = 0;
334 attr.border_pixel = 0;
335 attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
336 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
337 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
338
339 win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
340 0, visinfo->depth, InputOutput,
341 visinfo->visual, mask, &attr);
342 if (!win) {
343 Error("Couldn't create window");
344 }
345
346 {
347 XSizeHints sizehints;
348 sizehints.x = xpos;
349 sizehints.y = ypos;
350 sizehints.width = width;
351 sizehints.height = height;
352 sizehints.flags = USSize | USPosition;
353 XSetNormalHints(wt->Dpy, win, &sizehints);
354 XSetStandardProperties(wt->Dpy, win, "glthreads", "glthreads",
355 None, (char **)NULL, 0, &sizehints);
356 }
357
358
359 ctx = glXCreateContext(wt->Dpy, visinfo, NULL, True);
360 if (!ctx) {
361 Error("Couldn't create GLX context");
362 }
363
364 XMapWindow(wt->Dpy, win);
365 XSync(wt->Dpy, 0);
366
367 /* save the info for this window/context */
368 wt->Win = win;
369 wt->Context = ctx;
370 wt->Angle = 0.0;
371 wt->WinWidth = width;
372 wt->WinHeight = height;
373 wt->NewSize = GL_TRUE;
374}
375
376
377/*
378 * Called by pthread_create()
379 */
380static void *
381thread_function(void *p)
382{
383 struct winthread *wt = (struct winthread *) p;
384 draw_loop(wt);
385 return NULL;
386}
387
388
389/*
390 * called before exit to wait for all threads to finish
391 */
392static void
393clean_up(void)
394{
395 int i;
396
397 /* wait for threads to finish */
398 for (i = 0; i < NumWinThreads; i++) {
399 pthread_join(WinThreads[i].Thread, NULL);
400 }
401
402 for (i = 0; i < NumWinThreads; i++) {
403 glXDestroyContext(WinThreads[i].Dpy, WinThreads[i].Context);
404 XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
405 }
406}
407
408
Brian Paul12a317b2008-05-07 09:14:10 -0600409static void
410usage(void)
411{
412 printf("glthreads: test of GL thread safety (any key = exit)\n");
413 printf("Usage:\n");
414 printf(" glthreads [options]\n");
415 printf("Options:\n");
416 printf(" -display DISPLAYNAME Specify display string\n");
417 printf(" -n NUMTHREADS Number of threads to create\n");
418 printf(" -p Use a separate display connection for each thread\n");
419 printf(" -l Use application-side locking\n");
420}
421
Brian Paulc7d14442000-07-20 20:12:17 +0000422
423int
424main(int argc, char *argv[])
425{
426 char *displayName = ":0.0";
427 int numThreads = 2;
Brian Paul436d72e2006-03-23 16:58:22 +0000428 Display *dpy = NULL;
Brian Paulc7d14442000-07-20 20:12:17 +0000429 int i;
430 Status threadStat;
431
432 if (argc == 1) {
Brian Paul12a317b2008-05-07 09:14:10 -0600433 usage();
Brian Paulc7d14442000-07-20 20:12:17 +0000434 }
435 else {
436 int i;
437 for (i = 1; i < argc; i++) {
438 if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
439 displayName = argv[i + 1];
440 i++;
441 }
Brian Paul436d72e2006-03-23 16:58:22 +0000442 else if (strcmp(argv[i], "-p") == 0) {
443 MultiDisplays = 1;
444 }
Brian Paulc4f27102006-03-23 17:17:23 +0000445 else if (strcmp(argv[i], "-l") == 0) {
446 Locking = 1;
447 }
Brian Paulc7d14442000-07-20 20:12:17 +0000448 else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
449 numThreads = atoi(argv[i + 1]);
450 if (numThreads < 1)
451 numThreads = 1;
452 else if (numThreads > MAX_WINTHREADS)
453 numThreads = MAX_WINTHREADS;
454 i++;
455 }
Brian Paul436d72e2006-03-23 16:58:22 +0000456 else {
Brian Paul12a317b2008-05-07 09:14:10 -0600457 usage();
458 exit(1);
Brian Paul436d72e2006-03-23 16:58:22 +0000459 }
Brian Paulc7d14442000-07-20 20:12:17 +0000460 }
461 }
462
Brian Paulc4f27102006-03-23 17:17:23 +0000463 if (Locking)
Brian Paul12a317b2008-05-07 09:14:10 -0600464 printf("glthreads: Using explicit locks around Xlib calls.\n");
Brian Paulc4f27102006-03-23 17:17:23 +0000465 else
466 printf("glthreads: No explict locking.\n");
467
468 if (MultiDisplays)
469 printf("glthreads: Per-thread display connections.\n");
470 else
471 printf("glthreads: Single display connection.\n");
472
Brian Paulc7d14442000-07-20 20:12:17 +0000473 /*
474 * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
475 */
Brian Paul436d72e2006-03-23 16:58:22 +0000476 if (!MultiDisplays) {
Brian Paulc4f27102006-03-23 17:17:23 +0000477 if (!Locking) {
478 threadStat = XInitThreads();
Brian Paul436d72e2006-03-23 16:58:22 +0000479 if (threadStat) {
480 printf("XInitThreads() returned %d (success)\n", (int) threadStat);
481 }
482 else {
483 printf("XInitThreads() returned 0 (failure- this program may fail)\n");
484 }
Brian Paulc4f27102006-03-23 17:17:23 +0000485 }
Brian Paul436d72e2006-03-23 16:58:22 +0000486
487 dpy = XOpenDisplay(displayName);
488 if (!dpy) {
Brian73eee242006-12-13 08:30:26 -0700489 fprintf(stderr, "Unable to open display %s\n", XDisplayName(displayName));
Brian Paul436d72e2006-03-23 16:58:22 +0000490 return -1;
491 }
Brian Paulc7d14442000-07-20 20:12:17 +0000492 }
493
Brian Paulc4f27102006-03-23 17:17:23 +0000494 if (Locking) {
495 pthread_mutex_init(&Mutex, NULL);
496 }
497
Brian Paul436d72e2006-03-23 16:58:22 +0000498 printf("glthreads: creating windows\n");
Brian Paulc7d14442000-07-20 20:12:17 +0000499
500 NumWinThreads = numThreads;
501
502 /* Create the GLX windows and contexts */
503 for (i = 0; i < numThreads; i++) {
Brian Paul436d72e2006-03-23 16:58:22 +0000504 if (MultiDisplays) {
505 WinThreads[i].Dpy = XOpenDisplay(displayName);
506 assert(WinThreads[i].Dpy);
507 }
508 else {
509 WinThreads[i].Dpy = dpy;
510 }
Brian Paulc7d14442000-07-20 20:12:17 +0000511 WinThreads[i].Index = i;
512 create_window(&WinThreads[i]);
513 }
514
Brian Paul436d72e2006-03-23 16:58:22 +0000515 printf("glthreads: creating threads\n");
516
Brian Paulc7d14442000-07-20 20:12:17 +0000517 /* Create the threads */
518 for (i = 0; i < numThreads; i++) {
519 pthread_create(&WinThreads[i].Thread, NULL, thread_function,
520 (void*) &WinThreads[i]);
Brian96b4ac02008-04-03 20:12:32 -0600521 printf("glthreads: Created thread %p\n", (void *) WinThreads[i].Thread);
Brian Paulc7d14442000-07-20 20:12:17 +0000522 }
523
Brian Paul436d72e2006-03-23 16:58:22 +0000524 if (MultiDisplays)
525 event_loop_multi();
526 else
527 event_loop(dpy);
Brian Paulc7d14442000-07-20 20:12:17 +0000528
529 clean_up();
530
Brian Paul436d72e2006-03-23 16:58:22 +0000531 if (MultiDisplays) {
532 for (i = 0; i < numThreads; i++) {
533 XCloseDisplay(WinThreads[i].Dpy);
534 }
535 }
536 else {
537 XCloseDisplay(dpy);
538 }
Brian Paulc7d14442000-07-20 20:12:17 +0000539
540 return 0;
541}
542
543
544#else /* PTHREADS */
545
546
547#include <stdio.h>
548
549int
550main(int argc, char *argv[])
551{
552 printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
553 return 0;
554}
555
556
557#endif /* PTHREADS */