PDA

View Full Version : Sharing contexts with GLX for multithreaded app



Vylsain
05-15-2013, 02:59 AM
Hello everyone.

I'm programming a flight simulator app and I have data for the whole world.
Then, there is a lot of textures and geometry to load so I made a background thread that dynamically load all that data depending on my position on Earth.
Since I'm using SOIL to load my textures, I need to create a 2nd context for that background thread that will share OpenGL data with the main thread's context.

I use GLX to manage my window and I didn't find any example of this. I just know which functions to use but I'm facing some problems.

Here's what I do:

- init XLib thread support
- create the foreground context
- create the background context with the foreground one as shareList
- make the foreground context current for the rendering thread
- create the loader thread passing a structure containing the Display, the Window and the background context
- in the loader thread, make the background context current

It crashes with a segfault on the last step, when I call glXMakeCurrent with the Display, the Window and the thread context...

I'm doing something wrong but I don't know what and can't find solutions on forums.
Do you have any idea ?

Thank you for your help !

Here's my full code :

main


Display *dpy;
Window win;
GLXContext foregroundCtx, backgroundCtx;

int32_t main(int32_t argc, char** argv)
{
//init my own stuff (data paths, ini...)
initVisualSystem(argc, argv);

//create the window
initXDisplay();

glewInit();
//init my openGL stuff and create background thread
init(dpy, &backgroundCtx, &win, argc, argv);

event_loop(dpy, win);

cleanVisualSystem();

//XClean
glXDestroyContext(dpy, foregroundCtx);
glXDestroyContext(dpy, backgroundCtx);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);

return 0;
}


initXDisplay


void initXDisplay()
{
XInitThreads();

dpy = XOpenDisplay(NULL);
if (!dpy)
{
cerr << "Error: couldn't open display" << endl;
exit(1);
}

uint32_t width = renderer.getView()->getWidth();
uint32_t height = renderer.getView()->getHeight();

make_window(dpy, "XN3", 0, 0, width, height, &win, &foregroundCtx, &backgroundCtx);

XMapWindow(dpy, win);
glXMakeCurrent(dpy, win, foregroundCtx);

if (FULLSCREEN)
{
makeFullscreen(dpy, win);
}
hideCursor(dpy, win);
}


static void make_window( Display *dpy, const char *name,
int x, int y, int width, int height,
Window *winRet, GLXContext *foregroundCtxRet, GLXContext *backgroundCtxRet)
{
if(VSYNC)
putenv( (char *) "__GL_SYNC_TO_VBLANK=1" );
else
putenv( (char *) "__GL_SYNC_TO_VBLANK=0" );

int attrib[] = { GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DOUBLEBUFFER,
GLX_DEPTH_SIZE, 24,
GLX_SAMPLE_BUFFERS , 1*MULTISAMPLING, // <-- MSAA
GLX_SAMPLES , 4*MULTISAMPLING, // <-- MSAA
None };
int scrnum;
XSetWindowAttributes attr;
unsigned long mask;
Window root;
Window win;
GLXContext foregroundCtx, backgroundCtx;
XVisualInfo *visinfo;

scrnum = DefaultScreen( dpy );
root = RootWindow( dpy, scrnum );

visinfo = glXChooseVisual( dpy, scrnum, attrib );
if (!visinfo) {
printf("Error: couldn't get an RGB, Double-buffered visual\n");
exit(1);
}

/* window attributes */
attr.background_pixel = 0;
attr.border_pixel = 0;
attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPress | PointerMotionMask;
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

win = XCreateWindow( dpy, root, 0, 0, width, height,
0, visinfo->depth, InputOutput,
visinfo->visual, mask, &attr );

/* set hints and properties */
{
XSizeHints sizehints;
sizehints.x = x;
sizehints.y = y;
sizehints.width = width;
sizehints.height = height;
sizehints.flags = USSize | USPosition;
XSetNormalHints(dpy, win, &sizehints);
XSetStandardProperties(dpy, win, name, name,
None, (char **)NULL, 0, &sizehints);
}

foregroundCtx = glXCreateContext( dpy, visinfo, NULL, True );
if (!foregroundCtx) {
printf("Error: glXCreateContext for mainCtx failed\n");
exit(1);
}
backgroundCtx = glXCreateContext( dpy, visinfo, foregroundCtx, True );
if (!backgroundCtx) {
printf("Error: glXCreateContext for threadCtx failed\n");
exit(1);
}

XFree(visinfo);

*winRet = win;
*foregroundCtxRet = foregroundCtx;
*backgroundCtxRet = backgroundCtx;
}


init


void init(Display *dpy, GLXContext* backgroundCtx, Window* win, int32_t argc, char** argv)
{
//init my OpenGL stuff (shaders...)
...

SLoaderThread sLoaderThread;
sLoaderThread.pBV3DManager = renderer.getBV3DManager();
sLoaderThread.dpy = dpy;
sLoaderThread.backgroundCtx = backgroundCtx;
sLoaderThread.win = win;
pthread_create(&loaderThread, NULL, startLoaderThread, (void*)&sLoaderThread);
}

struct SLoaderThread
{
CBV3DManager* pBV3DManager; //manages my world geometries and textures
Display *dpy;
GLXContext* backgroundCtx;
Window* win;
};


void* startLoaderThread(void* data)
{
SLoaderThread* pSLoaderThread = (SLoaderThread*)data;

glXMakeCurrent(pSLoaderThread->dpy, *pSLoaderThread->win, *pSLoaderThread->backgroundCtx); //Crashes here with a segfault
while(1)
{
pSLoaderThread->pBV3DManager->updateZones();
}
return NULL;
}

mbentrup
05-15-2013, 05:06 AM
You pass the address of a stack-allocated structure to pthread_create, this will be destroyed as soon as the main thread leaves init(), so probably before the loader thread has a chance to read the stuff. Pass the data in static or malloc()ed memory.

Vylsain
05-15-2013, 05:59 AM
Oh ok. That's the first time I use theads and even though that's completely logical and the first thing to check, I missed that ! :whistle:

Thank you for your help mbentrup ! :)