mesa: add texturing to thread test

Run with -t to enable texture mapping.
Press 't' to update the texture image.  When the texture is changed in one
thread it should also get updated in the other threads since textures are
shared by all contexts.
This commit is contained in:
Brian Paul 2008-05-07 11:56:24 -06:00
parent ffe78987dc
commit f34a30bf79
1 changed files with 237 additions and 67 deletions

View File

@ -24,18 +24,33 @@
* This program tests GLX thread safety. * This program tests GLX thread safety.
* Command line options: * Command line options:
* -p Open a display connection for each thread * -p Open a display connection for each thread
* -l Enable application-side locking
* -n <num threads> Number of threads to create (default is 2) * -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 :0.0)
* -t Use texture mapping
* *
* Brian Paul 20 July 2000 * 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 */ #if defined(PTHREADS) /* defined by Mesa on Linux and other platforms */
#include <assert.h> #include <assert.h>
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -55,6 +70,8 @@ struct winthread {
float Angle; float Angle;
int WinWidth, WinHeight; int WinWidth, WinHeight;
GLboolean NewSize; GLboolean NewSize;
GLboolean Initialized;
GLboolean MakeNewTexture;
}; };
@ -65,8 +82,13 @@ static volatile GLboolean ExitFlag = GL_FALSE;
static GLboolean MultiDisplays = 0; static GLboolean MultiDisplays = 0;
static GLboolean Locking = 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_mutex_t Mutex;
static pthread_cond_t CondVar;
static pthread_mutex_t CondMutex;
static void static void
@ -77,6 +99,59 @@ Error(const char *msg)
} }
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 */ /* draw a colored cube */
static void static void
draw_object(void) draw_object(void)
@ -85,52 +160,61 @@ draw_object(void)
glScalef(0.75, 0.75, 0.75); glScalef(0.75, 0.75, 0.75);
glColor3f(1, 0, 0); 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); glColor3f(0, 1, 1);
glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
glVertex3f(-1, -1, -1); glTexCoord2f(1, 0); glVertex3f(-1, 1, -1);
glVertex3f(-1, 1, -1); glTexCoord2f(1, 1); glVertex3f(-1, 1, 1);
glVertex3f(-1, 1, 1); glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
glVertex3f(-1, -1, 1);
glEnd();
glColor3f(0, 1, 0); /* +X */
glBegin(GL_POLYGON); glColor3f(1, 0, 0);
glVertex3f(-1, 1, -1); glTexCoord2f(0, 0); glVertex3f(1, -1, -1);
glVertex3f( 1, 1, -1); glTexCoord2f(1, 0); glVertex3f(1, 1, -1);
glVertex3f( 1, 1, 1); glTexCoord2f(1, 1); glVertex3f(1, 1, 1);
glVertex3f(-1, 1, 1); glTexCoord2f(0, 1); glVertex3f(1, -1, 1);
glEnd();
/* -Y */
glColor3f(1, 0, 1); glColor3f(1, 0, 1);
glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
glVertex3f(-1, -1, -1); glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
glVertex3f( 1, -1, -1); glTexCoord2f(1, 1); glVertex3f( 1, -1, 1);
glVertex3f( 1, -1, 1); glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
glVertex3f(-1, -1, 1);
glEnd();
glColor3f(0, 0, 1); /* +Y */
glBegin(GL_POLYGON); glColor3f(0, 1, 0);
glVertex3f(-1, -1, 1); glTexCoord2f(0, 0); glVertex3f(-1, 1, -1);
glVertex3f( 1, -1, 1); glTexCoord2f(1, 0); glVertex3f( 1, 1, -1);
glVertex3f( 1, 1, 1); glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
glVertex3f(-1, 1, 1); glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
glEnd();
/* -Z */
glColor3f(1, 1, 0); glColor3f(1, 1, 0);
glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
glVertex3f(-1, -1, -1); glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
glVertex3f( 1, -1, -1); glTexCoord2f(1, 1); glVertex3f( 1, 1, -1);
glVertex3f( 1, 1, -1); glTexCoord2f(0, 1); glVertex3f(-1, 1, -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(); glEnd();
glPopMatrix(); glPopMatrix();
} }
@ -142,6 +226,8 @@ resize(struct winthread *wt, int w, int h)
wt->NewSize = GL_TRUE; wt->NewSize = GL_TRUE;
wt->WinWidth = w; wt->WinWidth = w;
wt->WinHeight = h; wt->WinHeight = h;
if (!Animate)
signal_redraw();
} }
@ -151,18 +237,19 @@ resize(struct winthread *wt, int w, int h)
static void static void
draw_loop(struct winthread *wt) draw_loop(struct winthread *wt)
{ {
GLboolean firstIter = GL_TRUE;
while (!ExitFlag) { while (!ExitFlag) {
if (Locking) if (Locking)
pthread_mutex_lock(&Mutex); pthread_mutex_lock(&Mutex);
glXMakeCurrent(wt->Dpy, wt->Win, wt->Context); glXMakeCurrent(wt->Dpy, wt->Win, wt->Context);
if (firstIter) { if (!wt->Initialized) {
printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index, printf("glthreads: %d: GL_RENDERER = %s\n", wt->Index,
(char *) glGetString(GL_RENDERER)); (char *) glGetString(GL_RENDERER));
firstIter = GL_FALSE; if (Texture /*&& wt->Index == 0*/) {
MakeNewTexture(wt);
}
wt->Initialized = GL_TRUE;
} }
if (Locking) if (Locking)
@ -182,10 +269,15 @@ draw_loop(struct winthread *wt)
wt->NewSize = GL_FALSE; wt->NewSize = GL_FALSE;
} }
if (wt->MakeNewTexture) {
MakeNewTexture(wt);
wt->MakeNewTexture = GL_FALSE;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); glPushMatrix();
glRotatef(wt->Angle, 0, 0, 1); glRotatef(wt->Angle, 0, 1, 0);
glRotatef(wt->Angle, 1, 0, 0); glRotatef(wt->Angle, 1, 0, 0);
glScalef(0.7, 0.7, 0.7); glScalef(0.7, 0.7, 0.7);
draw_object(); draw_object();
@ -199,12 +291,63 @@ draw_loop(struct winthread *wt)
if (Locking) if (Locking)
pthread_mutex_unlock(&Mutex); pthread_mutex_unlock(&Mutex);
usleep(5000); 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; 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. * The main process thread runs this loop.
* Single display connection for all threads. * Single display connection for all threads.
@ -250,10 +393,14 @@ event_loop(Display *dpy)
} }
break; break;
case KeyPress: case KeyPress:
/* tell all threads to exit */ for (i = 0; i < NumWinThreads; i++) {
ExitFlag = GL_TRUE; struct winthread *wt = &WinThreads[i];
/*printf("exit draw_loop %d\n", wt->Index);*/ if (event.xkey.window == wt->Win) {
return; keypress(&event, wt);
break;
}
}
break;
default: default:
/*no-op*/ ; /*no-op*/ ;
} }
@ -281,12 +428,10 @@ event_loop_multi(void)
resize(wt, event.xconfigure.width, event.xconfigure.height); resize(wt, event.xconfigure.width, event.xconfigure.height);
break; break;
case KeyPress: case KeyPress:
/* tell all threads to exit */ keypress(&event, wt);
ExitFlag = GL_TRUE; break;
/*printf("exit draw_loop %d\n", wt->Index);*/
return;
default: default:
/*no-op*/ ; ; /* nop */
} }
} }
w = (w + 1) % NumWinThreads; w = (w + 1) % NumWinThreads;
@ -300,7 +445,7 @@ event_loop_multi(void)
* we'll call this once for each thread, before the threads are created. * we'll call this once for each thread, before the threads are created.
*/ */
static void static void
create_window(struct winthread *wt) create_window(struct winthread *wt, GLXContext shareCtx)
{ {
Window win; Window win;
GLXContext ctx; GLXContext ctx;
@ -316,9 +461,9 @@ create_window(struct winthread *wt)
unsigned long mask; unsigned long mask;
Window root; Window root;
XVisualInfo *visinfo; XVisualInfo *visinfo;
int width = 80, height = 80; int width = 160, height = 160;
int xpos = (wt->Index % 10) * 90; int xpos = (wt->Index % 8) * (width + 10);
int ypos = (wt->Index / 10) * 100; int ypos = (wt->Index / 8) * (width + 20);
scrnum = DefaultScreen(wt->Dpy); scrnum = DefaultScreen(wt->Dpy);
root = RootWindow(wt->Dpy, scrnum); root = RootWindow(wt->Dpy, scrnum);
@ -355,7 +500,7 @@ create_window(struct winthread *wt)
} }
ctx = glXCreateContext(wt->Dpy, visinfo, NULL, True); ctx = glXCreateContext(wt->Dpy, visinfo, shareCtx, True);
if (!ctx) { if (!ctx) {
Error("Couldn't create GLX context"); Error("Couldn't create GLX context");
} }
@ -405,6 +550,23 @@ clean_up(void)
} }
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");
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
@ -416,9 +578,7 @@ main(int argc, char *argv[])
Status threadStat; Status threadStat;
if (argc == 1) { if (argc == 1) {
printf("glthreads: test of GL thread safety (any key = exit)\n"); usage();
printf("Usage:\n");
printf(" glthreads [-display dpyName] [-n numthreads]\n");
} }
else { else {
int i; int i;
@ -433,6 +593,9 @@ main(int argc, char *argv[])
else if (strcmp(argv[i], "-l") == 0) { else if (strcmp(argv[i], "-l") == 0) {
Locking = 1; Locking = 1;
} }
else if (strcmp(argv[i], "-t") == 0) {
Texture = 1;
}
else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
numThreads = atoi(argv[i + 1]); numThreads = atoi(argv[i + 1]);
if (numThreads < 1) if (numThreads < 1)
@ -442,13 +605,14 @@ main(int argc, char *argv[])
i++; i++;
} }
else { else {
fprintf(stderr, "glthreads: unexpected flag: %s\n", argv[i]); usage();
exit(1);
} }
} }
} }
if (Locking) if (Locking)
printf("glthreads: Using explict locks around Xlib calls.\n"); printf("glthreads: Using explicit locks around Xlib calls.\n");
else else
printf("glthreads: No explict locking.\n"); printf("glthreads: No explict locking.\n");
@ -478,9 +642,9 @@ main(int argc, char *argv[])
} }
} }
if (Locking) { pthread_mutex_init(&Mutex, NULL);
pthread_mutex_init(&Mutex, NULL); pthread_mutex_init(&CondMutex, NULL);
} pthread_cond_init(&CondVar, NULL);
printf("glthreads: creating windows\n"); printf("glthreads: creating windows\n");
@ -488,6 +652,8 @@ main(int argc, char *argv[])
/* Create the GLX windows and contexts */ /* Create the GLX windows and contexts */
for (i = 0; i < numThreads; i++) { for (i = 0; i < numThreads; i++) {
GLXContext share;
if (MultiDisplays) { if (MultiDisplays) {
WinThreads[i].Dpy = XOpenDisplay(displayName); WinThreads[i].Dpy = XOpenDisplay(displayName);
assert(WinThreads[i].Dpy); assert(WinThreads[i].Dpy);
@ -496,7 +662,11 @@ main(int argc, char *argv[])
WinThreads[i].Dpy = dpy; WinThreads[i].Dpy = dpy;
} }
WinThreads[i].Index = i; 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"); printf("glthreads: creating threads\n");
@ -505,7 +675,7 @@ main(int argc, char *argv[])
for (i = 0; i < numThreads; i++) { for (i = 0; i < numThreads; i++) {
pthread_create(&WinThreads[i].Thread, NULL, thread_function, pthread_create(&WinThreads[i].Thread, NULL, thread_function,
(void*) &WinThreads[i]); (void*) &WinThreads[i]);
printf("glthreads: Created thread %u\n", (unsigned int) WinThreads[i].Thread); printf("glthreads: Created thread %p\n", (void *) WinThreads[i].Thread);
} }
if (MultiDisplays) if (MultiDisplays)