blob: 333f4bb281adbb6ff84875fcf952c9f4b6a523e1 [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 Paulc7d14442000-07-20 20:12:17 +000027 * -n <num threads> Number of threads to create (default is 2)
28 * -display <display name> Specify X display (default is :0.0)
29 *
30 * Brian Paul 20 July 2000
31 */
32
33
34#if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
35
Brian Paul436d72e2006-03-23 16:58:22 +000036#include <assert.h>
Brian Paulc7d14442000-07-20 20:12:17 +000037#include <GL/gl.h>
38#include <GL/glx.h>
39#include <stdio.h>
40#include <stdlib.h>
Brian Paula3e44f42002-03-08 19:44:28 +000041#include <string.h>
Brian Paulc7d14442000-07-20 20:12:17 +000042#include <unistd.h>
43#include <pthread.h>
44
45
46/*
47 * Each window/thread/context:
48 */
49struct winthread {
50 Display *Dpy;
51 int Index;
52 pthread_t Thread;
53 Window Win;
54 GLXContext Context;
55 float Angle;
56 int WinWidth, WinHeight;
57 GLboolean NewSize;
58};
59
60
61#define MAX_WINTHREADS 100
62static struct winthread WinThreads[MAX_WINTHREADS];
63static int NumWinThreads = 0;
Brian Pauld07859e2004-07-02 14:35:05 +000064static volatile GLboolean ExitFlag = GL_FALSE;
Brian Paulc7d14442000-07-20 20:12:17 +000065
Brian Paul436d72e2006-03-23 16:58:22 +000066static GLboolean MultiDisplays = 0;
Brian Paulc7d14442000-07-20 20:12:17 +000067
68
69static void
70Error(const char *msg)
71{
72 fprintf(stderr, "Error: %s\n", msg);
73 exit(1);
74}
75
76
77/* draw a colored cube */
78static void
79draw_object(void)
80{
81 glPushMatrix();
82 glScalef(0.75, 0.75, 0.75);
83
84 glColor3f(1, 0, 0);
85 glBegin(GL_POLYGON);
86 glVertex3f(1, -1, -1);
87 glVertex3f(1, 1, -1);
88 glVertex3f(1, 1, 1);
89 glVertex3f(1, -1, 1);
90 glEnd();
91
92 glColor3f(0, 1, 1);
93 glBegin(GL_POLYGON);
94 glVertex3f(-1, -1, -1);
95 glVertex3f(-1, 1, -1);
96 glVertex3f(-1, 1, 1);
97 glVertex3f(-1, -1, 1);
98 glEnd();
99
100 glColor3f(0, 1, 0);
101 glBegin(GL_POLYGON);
102 glVertex3f(-1, 1, -1);
103 glVertex3f( 1, 1, -1);
104 glVertex3f( 1, 1, 1);
105 glVertex3f(-1, 1, 1);
106 glEnd();
107
108 glColor3f(1, 0, 1);
109 glBegin(GL_POLYGON);
110 glVertex3f(-1, -1, -1);
111 glVertex3f( 1, -1, -1);
112 glVertex3f( 1, -1, 1);
113 glVertex3f(-1, -1, 1);
114 glEnd();
115
116 glColor3f(0, 0, 1);
117 glBegin(GL_POLYGON);
118 glVertex3f(-1, -1, 1);
119 glVertex3f( 1, -1, 1);
120 glVertex3f( 1, 1, 1);
121 glVertex3f(-1, 1, 1);
122 glEnd();
123
124 glColor3f(1, 1, 0);
125 glBegin(GL_POLYGON);
126 glVertex3f(-1, -1, -1);
127 glVertex3f( 1, -1, -1);
128 glVertex3f( 1, 1, -1);
129 glVertex3f(-1, 1, -1);
130 glEnd();
131 glPopMatrix();
132}
133
134
135/* signal resize of given window */
136static void
137resize(struct winthread *wt, int w, int h)
138{
139 wt->NewSize = GL_TRUE;
140 wt->WinWidth = w;
141 wt->WinHeight = h;
142}
143
144
145/*
146 * We have an instance of this for each thread.
147 */
148static void
149draw_loop(struct winthread *wt)
150{
151 while (!ExitFlag) {
152
153 glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
154
155 glEnable(GL_DEPTH_TEST);
156
157 if (wt->NewSize) {
158 GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight;
159 glViewport(0, 0, wt->WinWidth, wt->WinHeight);
160 glMatrixMode(GL_PROJECTION);
161 glLoadIdentity();
162 glFrustum(-w, w, -1.0, 1.0, 1.5, 10);
163 glMatrixMode(GL_MODELVIEW);
164 glLoadIdentity();
165 glTranslatef(0, 0, -2.5);
166 wt->NewSize = GL_FALSE;
167 }
168
169 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
170
171 glPushMatrix();
172 glRotatef(wt->Angle, 0, 0, 1);
173 glRotatef(wt->Angle, 1, 0, 0);
174 glScalef(0.7, 0.7, 0.7);
175 draw_object();
176 glPopMatrix();
177
178 glXSwapBuffers(wt->Dpy, wt->Win);
179
Brian Paul436d72e2006-03-23 16:58:22 +0000180 usleep(5000);
Brian Paulc7d14442000-07-20 20:12:17 +0000181 wt->Angle += 1.0;
182 }
183}
184
185
186/*
187 * The main process thread runs this loop.
Brian Paul436d72e2006-03-23 16:58:22 +0000188 * Single display connection for all threads.
Brian Paulc7d14442000-07-20 20:12:17 +0000189 */
190static void
191event_loop(Display *dpy)
192{
Brian Pauld07859e2004-07-02 14:35:05 +0000193 XEvent event;
194 int i;
Brian Paulc7d14442000-07-20 20:12:17 +0000195
Brian Paul436d72e2006-03-23 16:58:22 +0000196 assert(!MultiDisplays);
197
Brian Pauld07859e2004-07-02 14:35:05 +0000198 while (!ExitFlag) {
199 XNextEvent(dpy, &event);
200 switch (event.type) {
201 case ConfigureNotify:
202 /* Find winthread for this event's window */
203 for (i = 0; i < NumWinThreads; i++) {
204 struct winthread *wt = &WinThreads[i];
205 if (event.xconfigure.window == wt->Win) {
206 resize(wt, event.xconfigure.width,
207 event.xconfigure.height);
208 break;
Brian Paulc7d14442000-07-20 20:12:17 +0000209 }
210 }
Brian Pauld07859e2004-07-02 14:35:05 +0000211 break;
212 case KeyPress:
213 /* tell all threads to exit */
214 ExitFlag = GL_TRUE;
215 /*printf("exit draw_loop %d\n", wt->Index);*/
216 return;
217 default:
218 /*no-op*/ ;
Brian Paulc7d14442000-07-20 20:12:17 +0000219 }
220 }
221}
222
223
224/*
Brian Paul436d72e2006-03-23 16:58:22 +0000225 * Separate display connection for each thread.
226 */
227static void
228event_loop_multi(void)
229{
230 XEvent event;
231 int w = 0;
232
233 assert(MultiDisplays);
234
235 while (!ExitFlag) {
236 struct winthread *wt = &WinThreads[w];
237 if (XPending(wt->Dpy)) {
238 XNextEvent(wt->Dpy, &event);
239 switch (event.type) {
240 case ConfigureNotify:
241 resize(wt, event.xconfigure.width, event.xconfigure.height);
242 break;
243 case KeyPress:
244 /* tell all threads to exit */
245 ExitFlag = GL_TRUE;
246 /*printf("exit draw_loop %d\n", wt->Index);*/
247 return;
248 default:
249 /*no-op*/ ;
250 }
251 }
252 w = (w + 1) % NumWinThreads;
253 usleep(5000);
254 }
255}
256
257
258
259/*
Brian Paulc7d14442000-07-20 20:12:17 +0000260 * we'll call this once for each thread, before the threads are created.
261 */
262static void
263create_window(struct winthread *wt)
264{
265 Window win;
266 GLXContext ctx;
267 int attrib[] = { GLX_RGBA,
268 GLX_RED_SIZE, 1,
269 GLX_GREEN_SIZE, 1,
270 GLX_BLUE_SIZE, 1,
271 GLX_DEPTH_SIZE, 1,
272 GLX_DOUBLEBUFFER,
273 None };
274 int scrnum;
275 XSetWindowAttributes attr;
276 unsigned long mask;
277 Window root;
278 XVisualInfo *visinfo;
279 int width = 80, height = 80;
280 int xpos = (wt->Index % 10) * 90;
281 int ypos = (wt->Index / 10) * 100;
282
283 scrnum = DefaultScreen(wt->Dpy);
284 root = RootWindow(wt->Dpy, scrnum);
285
286 visinfo = glXChooseVisual(wt->Dpy, scrnum, attrib);
287 if (!visinfo) {
288 Error("Unable to find RGB, Z, double-buffered visual");
289 }
290
291 /* window attributes */
292 attr.background_pixel = 0;
293 attr.border_pixel = 0;
294 attr.colormap = XCreateColormap(wt->Dpy, root, visinfo->visual, AllocNone);
295 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
296 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
297
298 win = XCreateWindow(wt->Dpy, root, xpos, ypos, width, height,
299 0, visinfo->depth, InputOutput,
300 visinfo->visual, mask, &attr);
301 if (!win) {
302 Error("Couldn't create window");
303 }
304
305 {
306 XSizeHints sizehints;
307 sizehints.x = xpos;
308 sizehints.y = ypos;
309 sizehints.width = width;
310 sizehints.height = height;
311 sizehints.flags = USSize | USPosition;
312 XSetNormalHints(wt->Dpy, win, &sizehints);
313 XSetStandardProperties(wt->Dpy, win, "glthreads", "glthreads",
314 None, (char **)NULL, 0, &sizehints);
315 }
316
317
318 ctx = glXCreateContext(wt->Dpy, visinfo, NULL, True);
319 if (!ctx) {
320 Error("Couldn't create GLX context");
321 }
322
323 XMapWindow(wt->Dpy, win);
324 XSync(wt->Dpy, 0);
325
326 /* save the info for this window/context */
327 wt->Win = win;
328 wt->Context = ctx;
329 wt->Angle = 0.0;
330 wt->WinWidth = width;
331 wt->WinHeight = height;
332 wt->NewSize = GL_TRUE;
333}
334
335
336/*
337 * Called by pthread_create()
338 */
339static void *
340thread_function(void *p)
341{
342 struct winthread *wt = (struct winthread *) p;
343 draw_loop(wt);
344 return NULL;
345}
346
347
348/*
349 * called before exit to wait for all threads to finish
350 */
351static void
352clean_up(void)
353{
354 int i;
355
356 /* wait for threads to finish */
357 for (i = 0; i < NumWinThreads; i++) {
358 pthread_join(WinThreads[i].Thread, NULL);
359 }
360
361 for (i = 0; i < NumWinThreads; i++) {
362 glXDestroyContext(WinThreads[i].Dpy, WinThreads[i].Context);
363 XDestroyWindow(WinThreads[i].Dpy, WinThreads[i].Win);
364 }
365}
366
367
368
369int
370main(int argc, char *argv[])
371{
372 char *displayName = ":0.0";
373 int numThreads = 2;
Brian Paul436d72e2006-03-23 16:58:22 +0000374 Display *dpy = NULL;
Brian Paulc7d14442000-07-20 20:12:17 +0000375 int i;
376 Status threadStat;
377
378 if (argc == 1) {
Brian Paul436d72e2006-03-23 16:58:22 +0000379 printf("threadgl: test of GL thread safety (any key = exit)\n");
Brian Paulc7d14442000-07-20 20:12:17 +0000380 printf("Usage:\n");
Brian Paul436d72e2006-03-23 16:58:22 +0000381 printf(" threadgl [-display dpyName] [-n numthreads]\n");
Brian Paulc7d14442000-07-20 20:12:17 +0000382 }
383 else {
384 int i;
385 for (i = 1; i < argc; i++) {
386 if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
387 displayName = argv[i + 1];
388 i++;
389 }
Brian Paul436d72e2006-03-23 16:58:22 +0000390 else if (strcmp(argv[i], "-p") == 0) {
391 MultiDisplays = 1;
392 }
Brian Paulc7d14442000-07-20 20:12:17 +0000393 else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
394 numThreads = atoi(argv[i + 1]);
395 if (numThreads < 1)
396 numThreads = 1;
397 else if (numThreads > MAX_WINTHREADS)
398 numThreads = MAX_WINTHREADS;
399 i++;
400 }
Brian Paul436d72e2006-03-23 16:58:22 +0000401 else {
402 fprintf(stderr, "glthreads: unexpected flag: %s\n", argv[i]);
403 }
Brian Paulc7d14442000-07-20 20:12:17 +0000404 }
405 }
406
407 /*
408 * VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
409 */
Brian Paul436d72e2006-03-23 16:58:22 +0000410 if (!MultiDisplays) {
411 threadStat = XInitThreads();
412 if (threadStat) {
413 printf("XInitThreads() returned %d (success)\n", (int) threadStat);
414 }
415 else {
416 printf("XInitThreads() returned 0 (failure- this program may fail)\n");
417 }
418
419 dpy = XOpenDisplay(displayName);
420 if (!dpy) {
421 fprintf(stderr, "Unable to open display %s\n", displayName);
422 return -1;
423 }
Brian Paulc7d14442000-07-20 20:12:17 +0000424 }
425
Brian Paul436d72e2006-03-23 16:58:22 +0000426 printf("glthreads: creating windows\n");
Brian Paulc7d14442000-07-20 20:12:17 +0000427
428 NumWinThreads = numThreads;
429
430 /* Create the GLX windows and contexts */
431 for (i = 0; i < numThreads; i++) {
Brian Paul436d72e2006-03-23 16:58:22 +0000432 if (MultiDisplays) {
433 WinThreads[i].Dpy = XOpenDisplay(displayName);
434 assert(WinThreads[i].Dpy);
435 }
436 else {
437 WinThreads[i].Dpy = dpy;
438 }
Brian Paulc7d14442000-07-20 20:12:17 +0000439 WinThreads[i].Index = i;
440 create_window(&WinThreads[i]);
441 }
442
Brian Paul436d72e2006-03-23 16:58:22 +0000443 printf("glthreads: creating threads\n");
444
Brian Paulc7d14442000-07-20 20:12:17 +0000445 /* Create the threads */
446 for (i = 0; i < numThreads; i++) {
447 pthread_create(&WinThreads[i].Thread, NULL, thread_function,
448 (void*) &WinThreads[i]);
Brian Paul436d72e2006-03-23 16:58:22 +0000449 printf("Created Thread %d\n", (int) WinThreads[i].Thread);
Brian Paulc7d14442000-07-20 20:12:17 +0000450 }
451
Brian Paul436d72e2006-03-23 16:58:22 +0000452 if (MultiDisplays)
453 event_loop_multi();
454 else
455 event_loop(dpy);
Brian Paulc7d14442000-07-20 20:12:17 +0000456
457 clean_up();
458
Brian Paul436d72e2006-03-23 16:58:22 +0000459 if (MultiDisplays) {
460 for (i = 0; i < numThreads; i++) {
461 XCloseDisplay(WinThreads[i].Dpy);
462 }
463 }
464 else {
465 XCloseDisplay(dpy);
466 }
Brian Paulc7d14442000-07-20 20:12:17 +0000467
468 return 0;
469}
470
471
472#else /* PTHREADS */
473
474
475#include <stdio.h>
476
477int
478main(int argc, char *argv[])
479{
480 printf("Sorry, this program wasn't compiled with PTHREADS defined.\n");
481 return 0;
482}
483
484
485#endif /* PTHREADS */