Sharing contexts with GLX for multithreaded app

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);
    [b]glXMakeCurrent(dpy, win, foregroundCtx);[/b]

    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
");
       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
");
       exit(1);
    }  
    backgroundCtx = glXCreateContext( dpy, visinfo, foregroundCtx, True );
    if (!backgroundCtx) {
       printf("Error: glXCreateContext for threadCtx failed
");
       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;
}

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.

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 ! :slight_smile:

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.