简体   繁体   中英

Creating OpenGL structures in a multithreaded program?

I am attempting to do the following in a physics engine I am building:

Have 2 threads, one for world logic, one for rendering.

The main thread (the thread from which the other threads are created) is the render thread, and then the world thread is forked from it.

On the render thread there is a global data structure called rendering handler declared as:

 class Renderer
{
    private:
        /*
          shader stuff
        */
        mutex busy_queue;
        vector<Render_Info*> render_queue;

    public:
        /*
           Bunch of methods
        */
        void add_data(Render_Info*);
        void render();
};

And a Render_Info structure is declared as:

struct Render_Info
{
    mutex info_lock;
    GLuint VAO;
    vector<GLuint> VBOs;
    vector<GLuint> types;
    uint layouts;
    uint render_instances;
    Mesh* geometry;
};

extern Renderer *Rendering_Handler;

The idea here was as follows. Any thread that wishes to render something, must handle it's own data an put it into OpenGL primitives. it then puts that information into a Render_Info object, which acts as a message between the thread and the rendering thread.

The thread then uses the add_data() method, to send a pointer to it's data message that gets appended to the render_queue as:

void Renderer::add_data(Render_Info* data)
{
    busy_queue.lock();
    render_queue.push_back(data);
    busy_queue.unlock();
}

And finally, when the render thread would choose to render something, it would lock the queue (preventing anything from being added to the queue) render everything, then clear the queue.

Now of course some more thread coordination is needed but that is the gist of the idea.

The issue is, I get segmentation faults just from trying to create OpenGL VAOs and VBOs, let alone filling them with data.

From what I have read OpenGL is as far away from being thread safe as a Giraffe is from being a dolphin.

And the cause of the problem seems to be that the OpenGL context belongs to the main thread, so when I try to create the VAOs and VBOs on the world thread OpenGL simply crashes as it has no idea what is going on.

What can I do do multi thread the program then?

I'd like to stay as close to the design I have described unless someone provides a good justification of why it would not work.

The requirement for OpenGL is that the context created for rendering should be owned by single thread at any given point and the thread that owns context should make it current and then call any gl related function. If you do that without owning and making context current then you get segmentation faults. By default the context will be current for the main thread. So to make your program multi threaded you have two options.

  1. Create two contexts and share resources like texture objects VAOs between them.Advantage of this approach is you can refer in thread 2 to any VAO created in thread 1 and it wont crash.

    Thread_1:

     glrc1=wglCreateContext(dc); glrc2=wglCreateContext(dc); BOOL error=wglShareLists(glrc1, glrc2); if(error == FALSE) { //Unable to share contexts so delete context and safe return } wglMakeCurrent(dc, glrc1); DoWork(); 

    Thread_2:

     wglMakeCurrent(dc, glrc2); DoWork(); 
  2. Other option is to make one context per thread and make it current when thread starts. Like following

    Thread_1:

     wglMakeCurrent(NULL, NULL); WaitForThread2(); OrDoSomeCPUJob(); wglMakeCurrent(dc, glrc); 

    Thread_2:

     wglMakeCurrent(dc, glrc); DoSome_GL_Work(); wglMakeCurrent(NULL, NULL); 

Hope this clears up the thing.

From what I have read OpenGL is as far away from being thread safe as a Giraffe is from being a dolphin.

Then you're misinformed. OpenGL is perfectly thread safe. You just have to keep in mind that OpenGL contexts act a bit like thread local storage. Ie when you make a OpenGL context current, then this is localized to the thread that makes that call.

Also extended OpenGL function pointers may be specific to a OpenGL context (but not to a context binding). However OpenGL function loaders keep a thread→context cache. So when you call an extended OpenGL function (ie one that must be loaded at runtime) from a thread without a context bound, you'll likely end up calling an invalid function pointer and get a crash.

However despite being perfectly thread safe, OpenGL does not necessarily gain performance when being used multithreaded. OpenGL thread zygote contexts are very useful if you need to update texture and buffer object data from a worker thread, but you should be careful not to tough things that might be in use by the main rendering thread. In programs where I have to do this, the usual approach is, that the data generating thread is creating a pool of texture/buffer objects, updates the data in them and then "surrenders" ownership of the object to the render thread. Eventually the render thread does its thing with these objects and once its done it passes the ownership back to the update thread and takes the next object from its own pool that gets filled with what the data thread sends over.

What can I do do multi thread the program then?

Create zygote OpenGL contexts and configure them to share their texture and buffer objects with the other thread(s) by the mechanism of display list sharing. You can have an arbitrary number of OpenGL contexts in your program and each thread can have its very own context active (while the other threads use different contexts).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM