-
/*
* Copyright (C) 2000 Brian Paul All Rights Reserved.
*
/*
* This program tests GLX thread safety.
* Command line options:
+ * -p Open a display connection for each thread
+ * -l Enable application-side locking
* -n <num threads> Number of threads to create (default is 2)
- * -display <display name> Specify X display (default is :0.0)
+ * -display <display name> Specify X display (default is $DISPLAY)
+ * -t Use texture mapping
*
* Brian Paul 20 July 2000
*/
+/*
+ * Notes:
+ * - Each thread gets its own GLX context.
+ *
+ * - The GLX contexts share texture objects.
+ *
+ * - When 't' is pressed to update the texture image, the window/thread which
+ * has input focus is signalled to change the texture. The other threads
+ * should see the updated texture the next time they call glBindTexture.
+ */
+
+
#if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
+#include <assert.h>
#include <GL/gl.h>
#include <GL/glx.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
float Angle;
int WinWidth, WinHeight;
GLboolean NewSize;
+ GLboolean Initialized;
+ GLboolean MakeNewTexture;
};
#define MAX_WINTHREADS 100
static struct winthread WinThreads[MAX_WINTHREADS];
static int NumWinThreads = 0;
-static GLboolean ExitFlag = GL_FALSE;
+static volatile GLboolean ExitFlag = GL_FALSE;
+
+static GLboolean MultiDisplays = 0;
+static GLboolean Locking = 0;
+static GLboolean Texture = GL_FALSE;
+static GLuint TexObj = 12;
+static GLboolean Animate = GL_TRUE;
+static pthread_mutex_t Mutex;
+static pthread_cond_t CondVar;
+static pthread_mutex_t CondMutex;
static void
}
+static void
+signal_redraw(void)
+{
+ pthread_mutex_lock(&CondMutex);
+ pthread_cond_broadcast(&CondVar);
+ pthread_mutex_unlock(&CondMutex);
+}
+
+
+static void
+MakeNewTexture(struct winthread *wt)
+{
+#define TEX_SIZE 128
+ static float step = 0.0;
+ GLfloat image[TEX_SIZE][TEX_SIZE][4];
+ GLint width;
+ int i, j;
+
+ for (j = 0; j < TEX_SIZE; j++) {
+ for (i = 0; i < TEX_SIZE; i++) {
+ float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE;
+ float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE;
+ float r = dt * dt + ds * ds + step;
+ image[j][i][0] =
+ image[j][i][1] =
+ image[j][i][2] = 0.75 + 0.25 * cos(r);
+ image[j][i][3] = 1.0;
+ }
+ }
+
+ step += 0.5;
+
+ glBindTexture(GL_TEXTURE_2D, TexObj);
+
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
+ if (width) {
+ assert(width == TEX_SIZE);
+ /* sub-tex replace */
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE,
+ GL_RGBA, GL_FLOAT, image);
+ }
+ else {
+ /* create new */
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
+ GL_RGBA, GL_FLOAT, image);
+ }
+}
+
+
+
/* draw a colored cube */
static void
draw_object(void)
glScalef(0.75, 0.75, 0.75);
glColor3f(1, 0, 0);
- glBegin(GL_POLYGON);
- glVertex3f(1, -1, -1);
- glVertex3f(1, 1, -1);
- glVertex3f(1, 1, 1);
- glVertex3f(1, -1, 1);
- glEnd();
+ if (Texture) {
+ glBindTexture(GL_TEXTURE_2D, TexObj);
+ glEnable(GL_TEXTURE_2D);
+ }
+ else {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glBegin(GL_QUADS);
+
+ /* -X */
glColor3f(0, 1, 1);
- glBegin(GL_POLYGON);
- glVertex3f(-1, -1, -1);
- glVertex3f(-1, 1, -1);
- glVertex3f(-1, 1, 1);
- glVertex3f(-1, -1, 1);
- glEnd();
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
+ glTexCoord2f(1, 0); glVertex3f(-1, 1, -1);
+ glTexCoord2f(1, 1); glVertex3f(-1, 1, 1);
+ glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
- glColor3f(0, 1, 0);
- glBegin(GL_POLYGON);
- glVertex3f(-1, 1, -1);
- glVertex3f( 1, 1, -1);
- glVertex3f( 1, 1, 1);
- glVertex3f(-1, 1, 1);
- glEnd();
+ /* +X */
+ glColor3f(1, 0, 0);
+ glTexCoord2f(0, 0); glVertex3f(1, -1, -1);
+ glTexCoord2f(1, 0); glVertex3f(1, 1, -1);
+ glTexCoord2f(1, 1); glVertex3f(1, 1, 1);
+ glTexCoord2f(0, 1); glVertex3f(1, -1, 1);
+ /* -Y */
glColor3f(1, 0, 1);
- glBegin(GL_POLYGON);
- glVertex3f(-1, -1, -1);
- glVertex3f( 1, -1, -1);
- glVertex3f( 1, -1, 1);
- glVertex3f(-1, -1, 1);
- glEnd();
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
+ glTexCoord2f(1, 1); glVertex3f( 1, -1, 1);
+ glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
- glColor3f(0, 0, 1);
- glBegin(GL_POLYGON);
- glVertex3f(-1, -1, 1);
- glVertex3f( 1, -1, 1);
- glVertex3f( 1, 1, 1);
- glVertex3f(-1, 1, 1);
- glEnd();
+ /* +Y */
+ glColor3f(0, 1, 0);
+ glTexCoord2f(0, 0); glVertex3f(-1, 1, -1);
+ glTexCoord2f(1, 0); glVertex3f( 1, 1, -1);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
+ /* -Z */
glColor3f(1, 1, 0);
- glBegin(GL_POLYGON);
- glVertex3f(-1, -1, -1);
- glVertex3f( 1, -1, -1);
- glVertex3f( 1, 1, -1);
- glVertex3f(-1, 1, -1);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, -1);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, -1);
+
+ /* +Y */
+ glColor3f(0, 0, 1);
+ glTexCoord2f(0, 0); glVertex3f(-1, -1, 1);
+ glTexCoord2f(1, 0); glVertex3f( 1, -1, 1);
+ glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
+ glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
+
glEnd();
+
glPopMatrix();
}
wt->NewSize = GL_TRUE;
wt->WinWidth = w;
wt->WinHeight = h;
+ if (!Animate)
+ signal_redraw();
}
{
while (!ExitFlag) {
+ if (Locking)
+ pthread_mutex_lock(&Mutex);
+
glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
+ if (!wt->Initialized) {
+ printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index,
+ (char *) glGetString(GL_RENDERER));
+ if (Texture /*&& wt->Index == 0*/) {
+ MakeNewTexture(wt);
+ }
+ wt->Initialized = GL_TRUE;
+ }
+
+ if (Locking)
+ pthread_mutex_unlock(&Mutex);
glEnable(GL_DEPTH_TEST);
wt->NewSize = GL_FALSE;
}
+ if (wt->MakeNewTexture) {
+ MakeNewTexture(wt);
+ wt->MakeNewTexture = GL_FALSE;
+ }
+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
- glRotatef(wt->Angle, 0, 0, 1);
+ glRotatef(wt->Angle, 0, 1, 0);
glRotatef(wt->Angle, 1, 0, 0);
glScalef(0.7, 0.7, 0.7);
draw_object();
glPopMatrix();
+ if (Locking)
+ pthread_mutex_lock(&Mutex);
+
glXSwapBuffers(wt->Dpy, wt->Win);
+ if (Locking)
+ pthread_mutex_unlock(&Mutex);
+
+ if (Animate) {
+ usleep(5000);
+ }
+ else {
+ /* wait for signal to draw */
+ pthread_mutex_lock(&CondMutex);
+ pthread_cond_wait(&CondVar, &CondMutex);
+ pthread_mutex_unlock(&CondMutex);
+ }
wt->Angle += 1.0;
}
}
+static void
+keypress(XEvent *event, struct winthread *wt)
+{
+ char buf[100];
+ KeySym keySym;
+ XComposeStatus stat;
+
+ XLookupString(&event->xkey, buf, sizeof(buf), &keySym, &stat);
+
+ switch (keySym) {
+ case XK_Escape:
+ /* tell all threads to exit */
+ if (!Animate) {
+ signal_redraw();
+ }
+ ExitFlag = GL_TRUE;
+ /*printf("exit draw_loop %d\n", wt->Index);*/
+ return;
+ case XK_t:
+ case XK_T:
+ if (Texture) {
+ wt->MakeNewTexture = GL_TRUE;
+ if (!Animate)
+ signal_redraw();
+ }
+ break;
+ case XK_a:
+ case XK_A:
+ Animate = !Animate;
+ if (Animate) /* yes, prev Animate state! */
+ signal_redraw();
+ break;
+ case XK_s:
+ case XK_S:
+ if (!Animate)
+ signal_redraw();
+ break;
+ default:
+ ; /* nop */
+ }
+}
+
+
/*
* The main process thread runs this loop.
+ * Single display connection for all threads.
*/
static void
event_loop(Display *dpy)
{
+ XEvent event;
+ int i;
+
+ assert(!MultiDisplays);
+
while (!ExitFlag) {
- static long mask = StructureNotifyMask | ExposureMask | KeyPressMask;
- XEvent event;
- int i;
- for (i = 0; i < NumWinThreads; i++) {
- struct winthread *wt = &WinThreads[i];
- while (XCheckWindowEvent(dpy, wt->Win, mask, &event)) {
- if (event.xany.window == wt->Win) {
- switch (event.type) {
- case ConfigureNotify:
- resize(wt, event.xconfigure.width,
- event.xconfigure.height);
- break;
- case KeyPress:
- /* tell all threads to exit */
- ExitFlag = GL_TRUE;
- /*printf("exit draw_loop %d\n", wt->Index);*/
- return;
- default:
- /*no-op*/ ;
+ if (Locking) {
+ while (1) {
+ int k;
+ pthread_mutex_lock(&Mutex);
+ k = XPending(dpy);
+ if (k) {
+ XNextEvent(dpy, &event);
+ pthread_mutex_unlock(&Mutex);
+ break;
+ }
+ pthread_mutex_unlock(&Mutex);
+ usleep(5000);
+ }
+ }
+ else {
+ XNextEvent(dpy, &event);
+ }
+
+ switch (event.type) {
+ case ConfigureNotify:
+ /* Find winthread for this event's window */
+ for (i = 0; i < NumWinThreads; i++) {
+ struct winthread *wt = &WinThreads[i];
+ if (event.xconfigure.window == wt->Win) {
+ resize(wt, event.xconfigure.width,
+ event.xconfigure.height);
+ break;
}
}
- else {
- printf("window mismatch\n");
+ break;
+ case KeyPress:
+ for (i = 0; i < NumWinThreads; i++) {
+ struct winthread *wt = &WinThreads[i];
+ if (event.xkey.window == wt->Win) {
+ keypress(&event, wt);
+ break;
+ }
}
+ break;
+ default:
+ /*no-op*/ ;
+ }
+ }
+}
+
+
+/*
+ * Separate display connection for each thread.
+ */
+static void
+event_loop_multi(void)
+{
+ XEvent event;
+ int w = 0;
+
+ assert(MultiDisplays);
+
+ while (!ExitFlag) {
+ struct winthread *wt = &WinThreads[w];
+ if (XPending(wt->Dpy)) {
+ XNextEvent(wt->Dpy, &event);
+ switch (event.type) {
+ case ConfigureNotify:
+ resize(wt, event.xconfigure.width, event.xconfigure.height);
+ break;
+ case KeyPress:
+ keypress(&event, wt);
+ break;
+ default:
+ ; /* nop */
}
}
+ w = (w + 1) % NumWinThreads;
+ usleep(5000);
}
}
+
/*
* we'll call this once for each thread, before the threads are created.
*/
static void
-create_window(struct winthread *wt)
+create_window(struct winthread *wt, GLXContext shareCtx)
{
Window win;
GLXContext ctx;
unsigned long mask;
Window root;
XVisualInfo *visinfo;
- int width = 80, height = 80;
- int xpos = (wt->Index % 10) * 90;
- int ypos = (wt->Index / 10) * 100;
+ int width = 160, height = 160;
+ int xpos = (wt->Index % 8) * (width + 10);
+ int ypos = (wt->Index / 8) * (width + 20);
scrnum = DefaultScreen(wt->Dpy);
root = RootWindow(wt->Dpy, scrnum);
}
- ctx = glXCreateContext(wt->Dpy, visinfo, NULL, True);
+ ctx = glXCreateContext(wt->Dpy, visinfo, shareCtx, True);
if (!ctx) {
Error("Couldn't create GLX context");
}
}
+static void
+usage(void)
+{
+ printf("glthreads: test of GL thread safety (any key = exit)\n");
+ printf("Usage:\n");
+ printf(" glthreads [options]\n");
+ printf("Options:\n");
+ printf(" -display DISPLAYNAME Specify display string\n");
+ printf(" -n NUMTHREADS Number of threads to create\n");
+ printf(" -p Use a separate display connection for each thread\n");
+ printf(" -l Use application-side locking\n");
+ printf(" -t Enable texturing\n");
+ printf("Keyboard:\n");
+ printf(" Esc Exit\n");
+ printf(" t Change texture image (requires -t option)\n");
+ printf(" a Toggle animation\n");
+ printf(" s Step rotation (when not animating)\n");
+}
+
int
main(int argc, char *argv[])
{
- char *displayName = ":0.0";
+ char *displayName = NULL;
int numThreads = 2;
- Display *dpy;
+ Display *dpy = NULL;
int i;
Status threadStat;
if (argc == 1) {
- printf("threadgl: test of GL thread safety (any key = exit)\n");
- printf("Usage:\n");
- printf(" threadgl [-display dpyName] [-n numthreads]\n");
+ usage();
}
else {
int i;
displayName = argv[i + 1];
i++;
}
+ else if (strcmp(argv[i], "-p") == 0) {
+ MultiDisplays = 1;
+ }
+ else if (strcmp(argv[i], "-l") == 0) {
+ Locking = 1;
+ }
+ else if (strcmp(argv[i], "-t") == 0) {
+ Texture = 1;
+ }
else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
numThreads = atoi(argv[i + 1]);
if (numThreads < 1)
numThreads = MAX_WINTHREADS;
i++;
}
+ else {
+ usage();
+ exit(1);
+ }
}
}
+ if (Locking)
+ printf("glthreads: Using explicit locks around Xlib calls.\n");
+ else
+ printf("glthreads: No explict locking.\n");
+
+ if (MultiDisplays)
+ printf("glthreads: Per-thread display connections.\n");
+ else
+ printf("glthreads: Single display connection.\n");
+
/*
* VERY IMPORTANT: call XInitThreads() before any other Xlib functions.
*/
- threadStat = XInitThreads();
- if (threadStat) {
- printf("XInitThreads() returned %d (success)\n", (int) threadStat);
- }
- else {
- printf("XInitThreads() returned 0 (failure- this program may fail)\n");
+ if (!MultiDisplays) {
+ if (!Locking) {
+ threadStat = XInitThreads();
+ if (threadStat) {
+ printf("XInitThreads() returned %d (success)\n", (int) threadStat);
+ }
+ else {
+ printf("XInitThreads() returned 0 (failure- this program may fail)\n");
+ }
+ }
+
+ dpy = XOpenDisplay(displayName);
+ if (!dpy) {
+ fprintf(stderr, "Unable to open display %s\n", XDisplayName(displayName));
+ return -1;
+ }
}
+ pthread_mutex_init(&Mutex, NULL);
+ pthread_mutex_init(&CondMutex, NULL);
+ pthread_cond_init(&CondVar, NULL);
- dpy = XOpenDisplay(displayName);
- if (!dpy) {
- fprintf(stderr, "Unable to open display %s\n", displayName);
- return -1;
- }
+ printf("glthreads: creating windows\n");
NumWinThreads = numThreads;
/* Create the GLX windows and contexts */
for (i = 0; i < numThreads; i++) {
- WinThreads[i].Dpy = dpy;
+ GLXContext share;
+
+ if (MultiDisplays) {
+ WinThreads[i].Dpy = XOpenDisplay(displayName);
+ assert(WinThreads[i].Dpy);
+ }
+ else {
+ WinThreads[i].Dpy = dpy;
+ }
WinThreads[i].Index = i;
- create_window(&WinThreads[i]);
+ WinThreads[i].Initialized = GL_FALSE;
+
+ share = (Texture && i > 0) ? WinThreads[0].Context : 0;
+
+ create_window(&WinThreads[i], share);
}
+ printf("glthreads: creating threads\n");
+
/* Create the threads */
for (i = 0; i < numThreads; i++) {
pthread_create(&WinThreads[i].Thread, NULL, thread_function,
(void*) &WinThreads[i]);
- printf("Created Thread %p\n", WinThreads[i].Thread);
+ printf("glthreads: Created thread %p\n", (void *) WinThreads[i].Thread);
}
- event_loop(dpy);
+ if (MultiDisplays)
+ event_loop_multi();
+ else
+ event_loop(dpy);
clean_up();
- XCloseDisplay(dpy);
+ if (MultiDisplays) {
+ for (i = 0; i < numThreads; i++) {
+ XCloseDisplay(WinThreads[i].Dpy);
+ }
+ }
+ else {
+ XCloseDisplay(dpy);
+ }
return 0;
}