From: John S. <fre...@ro...> - 2003-11-14 15:05:57
Attachments:
test.c
|
Dear Mesa-ites; I am having real problems using Mesa and threads under Linux. I have the following program (modified from one from the net that was c++) that compiles and runs on a SGI running Onyx, but fails with an X error on Linux. Can anyone shed any light on what is going wrong? I compile it on my Suse 8.1 box with: gcc -o threaddemo test.c -lGL -lX11 -lm -lpthread \ -L/usr/X11R6/lib Thanks for any help anyone can give. John Stewart CRC Canada. |
From: Wes B. <ewb...@r3...> - 2003-11-14 15:33:44
|
I took a quick look at your program and have two suggestions. 1. Double check your GLX documentation to make sure that it is legal to have two OpenGL contexts bound to the same window. I believe such a thing is not permitted, but you should double check. 2. If it were me, I'd redesign this program so that there is only one OpenGL context that is shared by the two rendering threads, rather than having each thread create its own OpenGL context. In the same vein, since there is only one window, you only need one SwapBuffers call, rather than the two that are currently being executed. The GLX documentation is quite explicit about context ownership in a multithreaded program - study that stuff carefully. You'll use your existing mutex to permit only one thread at a time to issue OpenGL calls. Logically, this approach is the same as the program below, which assumes that OpenGL serializes calls from multiple threads. OpenGL doesn't do that, and is not supposed to do that. Basically, your problems stem from "abusing" the OpenGL context in a multithread setting. As for it working on the SGI, all I can say is "good job to the SGI guys for building a system that permits such abuse." HTH, wes John Stewart wrote: > Dear Mesa-ites; > > I am having real problems using Mesa and threads under > Linux. I have the following program (modified from one > from the net that was c++) that compiles and runs on a > SGI running Onyx, but fails with an X error on Linux. > > Can anyone shed any light on what is going wrong? > > I compile it on my Suse 8.1 box with: > > gcc -o threaddemo test.c -lGL -lX11 -lm -lpthread \ > -L/usr/X11R6/lib > > Thanks for any help anyone can give. > > John Stewart > CRC Canada. > > > ------------------------------------------------------------------------ > > #include <unistd.h> > #include <math.h> > #include <GL/gl.h> > #include <GL/glx.h> > #include <X11/Xutil.h> > #include <pthread.h> > > // Define a mutex and LOCK/UNLOCK macros > > pthread_mutex_t _renderlock = PTHREAD_MUTEX_INITIALIZER; > #define LOCK() pthread_mutex_lock(&_renderlock) > #define UNLOCK() pthread_mutex_unlock(&_renderlock) > > // Define this to enable double buffering. > #define DOUBLE_BUFFER > > // Globals shared between threads > > Display *dpy; > XVisualInfo *visual; > Colormap cmap; > Window window; > int width = 300, height = 300; > > // GL attributes > static int visualAttr[] = { > GLX_RGBA, > #ifdef DOUBLE_BUFFER > GLX_DOUBLEBUFFER, > #endif > GLX_RED_SIZE, 1, GLX_DEPTH_SIZE, 1, None, > }; > > // A rendering class that draws an object in a separate thread. > > struct Painter { > int _x; > GLXContext ctx; > int count; > }; > > void Painter_draw(struct Painter *data) > { > // Just update a single frame of this image > GLfloat radius = 0.60; > int s, numSides = 32; > > GLfloat rotx = 6.0 * data->count; > GLfloat roty = 7.0 * data->count; > > data->count++; > > if (rotx > 360) rotx -= 360; > if (roty > 360) roty -= 360; > > glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); > > glPushMatrix(); > glRotatef(rotx, 1, 0, 0); > glRotatef(roty, 0, 1, 0); > > > glColor3f(0.8, 0.5, 0.1); > > glBegin(GL_TRIANGLE_STRIP); > for (s=0; s<numSides+1; ++s) { > double a = s * (2*M_PI / numSides); > GLfloat x = radius * (GLfloat) cos(a); > GLfloat y = radius * (GLfloat) sin(a); > glNormal3f(x, y, 0.0); > glVertex3f(x, y, 0.60); > glVertex3f(x, y,-0.60); > } > glEnd(); > > glPopMatrix(); > } > > void Painter_run(struct Painter *obj) > { > // Must create a context, bound in this thread > > LOCK(); > printf ("creating context\n"); > obj->ctx = glXCreateContext(dpy, visual, NULL, True); > printf ("Context created, %d\n",obj->ctx); > glXMakeCurrent(dpy, window, obj->ctx); > printf ("current made\n"); > UNLOCK(); > > // Set up GL parameters > > glEnable(GL_LIGHT0); > glEnable(GL_LIGHTING); > glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); > glEnable(GL_COLOR_MATERIAL); > glEnable(GL_DEPTH_TEST); > glMatrixMode(GL_PROJECTION); > glFrustum(-1, 1, -1, 1, 1, 3); > glMatrixMode(GL_MODELVIEW); > glTranslatef(0, 0, -2); > glClearColor(0.0, 0.0, 0.0, 1.0); > > // glViewPort may make X calls, so lock it > LOCK(); > glViewport(obj->_x, 0, width, height); > UNLOCK(); > > // Draw in a loop, locking on swapping buffers > while (1) > { > Painter_draw(obj); > > #ifdef DOUBLE_BUFFER > LOCK(); > glXSwapBuffers(dpy, window); > UNLOCK(); > #endif > } > } > > // Create an X window and enter a loop in the main thread. > > int main(int argc, char **argv) > { > int scrn; > Window root; > XSetWindowAttributes swa; > XSizeHints sizeHints; > XEvent ev; > pthread_t ptid; > struct Painter p, q; > > dpy = XOpenDisplay(NULL); > scrn = DefaultScreen(dpy); > > root = RootWindow(dpy, scrn); > > visual = glXChooseVisual(dpy, scrn, visualAttr); > > cmap = XCreateColormap(dpy, root, visual->visual, > AllocNone); > > swa.background_pixel = None; > swa.border_pixel = None; > swa.colormap = cmap; > > > printf ("creating window\n"); > window = XCreateWindow(dpy, root, 0, 0, 300, 300, 0, > visual->depth, InputOutput, visual->visual, > CWBackPixel | CWBorderPixel | > CWColormap | CWEventMask, &swa); > printf ("window created, it is %d\n",window); > > sizeHints.flags = PSize; > sizeHints.width = width; > sizeHints.height = height; > > //XSetStandardProperties(dpy, window, "demo", "demo", None, > // NULL, 0, &sizeHints); > > XMapWindow(dpy, window); > > // Create the rendering thread > printf ("Window mapped\n"); > > LOCK(); > p._x = 0; > pthread_create(&ptid, 0, (void *(*)(void *))&Painter_run, (void*) &p); > > q._x = 300; > pthread_create(&ptid, 0, (void *(*)(void *))&Painter_run, (void*) &q); > UNLOCK(); > > while (1) { > > LOCK(); > > while (XPending(dpy)) { > > XNextEvent(dpy, &ev); > switch (ev.type) { > case ConfigureNotify: > width = ev.xconfigure.width/2; > height = ev.xconfigure.height; > break; > default: > break; > } > } > > UNLOCK(); > sleep(1); > } > } -- Wes Bethel -- R3vis Corporation -- voice (415) 898-0814 -- www.r3vis.com |
From: Brian P. <br...@tu...> - 2003-11-14 15:50:19
|
Wes Bethel wrote: > > I took a quick look at your program and have two suggestions. > > 1. Double check your GLX documentation to make sure that it is legal to > have two OpenGL contexts bound to the same window. I believe such a > thing is not permitted, but you should double check. Actually, you can do that. > 2. If it were me, I'd redesign this program so that there is only one > OpenGL context that is shared by the two rendering threads, rather than > having each thread create its own OpenGL context. In the same vein, > since there is only one window, you only need one SwapBuffers call, > rather than the two that are currently being executed. The GLX > documentation is quite explicit about context ownership in a > multithreaded program - study that stuff carefully. You'll use your > existing mutex to permit only one thread at a time to issue OpenGL > calls. Logically, this approach is the same as the program below, which > assumes that OpenGL serializes calls from multiple threads. OpenGL > doesn't do that, and is not supposed to do that. If you want two threads to render into the same window region you'll need to synchronize them and only issue one glXSwapBuffers when you're done. If you want to draw to disjoint window regions, you might consider making separate sub windows for each thread, and call glXSwapBuffers on each subwindow. > Basically, your problems stem from "abusing" the OpenGL context in a > multithread setting. As for it working on the SGI, all I can say is > "good job to the SGI guys for building a system that permits such abuse." On the SGI system, I think John's just getting lucky with the initialization of swa.event_mask. Valgrind or another memory checker would have caught that. -Brian |
From: Brian P. <br...@tu...> - 2003-11-14 15:38:50
|
John Stewart wrote: > Dear Mesa-ites; > > I am having real problems using Mesa and threads under > Linux. I have the following program (modified from one > from the net that was c++) that compiles and runs on a > SGI running Onyx, but fails with an X error on Linux. > > Can anyone shed any light on what is going wrong? Well, you've got a subtle bug in your window creation code. See below. > I compile it on my Suse 8.1 box with: > > gcc -o threaddemo test.c -lGL -lX11 -lm -lpthread \ > -L/usr/X11R6/lib > > Thanks for any help anyone can give. The program's running here, but I think there's other issues. First, the viewport calculation isn't reliable. Sometimes the width is 150, sometimes it's 300. If you study the code for a bit I think you'll see why. Next, both rendering threads are calling glXSwapBuffers on the same window without any sort of synchronization. That causes random flickering. Here's the X error: > swa.background_pixel = None; > swa.border_pixel = None; > swa.colormap = cmap; You also need to set swa.event_mask = StructureNotifyMask; here since in the XCreateWindow call you're passing the CWEventMask flag. Otherwise, swa.event_mask is a random value. > > > printf ("creating window\n"); > window = XCreateWindow(dpy, root, 0, 0, 300, 300, 0, > visual->depth, InputOutput, visual->visual, > CWBackPixel | CWBorderPixel | > CWColormap | CWEventMask, &swa); > printf ("window created, it is %d\n",window); -Brian |