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.
Guidelines for Threading OpenGL ApplicationsUse only one thread per context. OpenGL commands for a specific context are not thread safe. You should never have more than one thread accessing a single context simultaneously. Contexts that are on different threads can share object resources.
Can I call SDL video functions from multiple threads? No, most graphics back ends are not thread-safe, so you should only call SDL video functions from the main thread of your application.
Starting with C++11 C++ has classes for multithreading support. The class you might be interested in most is std::thread . There are also classes for synchronization like std::mutex .
C/C++ Languages Now Include Multithreading Libraries Moving from single-threaded programs to multithreaded increases complexity. Programming languages, such as C and C++, have evolved to make it easier to use multiple threads and handle this complexity. Both C and C++ now include threading libraries.
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.
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();
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With