I'm initializing my OpenGL context(SDL/GLEW) in the main thread. But rendering is done in a different thread(things like SDL_GL_SwapWindow or glDrawElements).
The thing is, that nothing happens if I call SDL_GL_SwapWindow. I was trying to simply change the clear color after every swap, but nothing happens.
void render(){
// Rendering...
}
int main(){
// Initialization...
thread rendering(render);
}
Could this be problem?
Not going to work as you expect. OpenGL Context is thread local. Whichever thread you create the context is where the actual OpenGL rendering calls will have to be made from.
Related
I am making my first game and I am stuck with one problem. I have a world where you can walk free, but then when you meet the enemy you will switch to battle and when you are switching to battle, I need to load all the models that will be rendered in the battle scene. The loading takes about ~5 seconds and I want to make the loading screen. So, I rendered the loading screen in the main thread, but how can I load 3d models and build different VAO and VBO at the same time? I made a new thread for this loading, but I read online "don't use threads for generating VAOs". What is the best solution to make this loading? Should I just preload all the models in the main thread before the game starts? Personally, for me it seems not right to load all the 3d models in the beginning of the game.
Assuming you have two windows, you can bind each context of the window to separate threads. Problems will arise if you share data between them (proper locking is mandatory).
See glfwMakeContextCurrent:
This function makes the OpenGL or OpenGL ES context of the specified window current on the calling thread. A context must only be made current on a single thread at a time and each thread can have only a single current context at a time.
Thread safety:This function may be called from any thread.
See glfwSwapBuffers:
This function swaps the front and back buffers of the specified window when rendering with OpenGL or OpenGL ES.
Thread safety:This function may be called from any thread.
Some functions in GLFW can only be called from the 'main' thread (nor from callbacks), e.g. glfwPollEvents, but other than that, bind the context to a thread, perform your OpenGl calls and swap the buffers. As said before, as long as you don't share any buffers, there should be no problem.
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.
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.
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. I.e. 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 (i.e. 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).
I'm using SDL2 and OpenGL 3.3, and wondering if following situations are thread safe:
case 1
There are multiple threads and each of them has a context and a SDL window.
Those threads render geometries multi-threaded.
case 2
There are multiple threads and each of them has a SDL window. But there's only one context that is shared.
Those threads render geometries multi-threaded.
I guess latter case is not safe because threads change current context by calling 'SDL_GL_MakeCurrent', but I'm not sure.
Ok I was silly.
I was reading documents and found it:
SDL_GL_GetCurrentContext: Use this function to get the currently active OpenGL context.
SDL_GLContext SDL_GL_GetCurrentContext(void)
And realized that there's only one OpenGL context activated, which means without calling 'SDL_GL_MakeCurrent' rendering with multiple context is impossible. That is, the first case is barely different to the second case.
So I concluded it's not thread safe. Sorry for my silliness.
I am working on a project that will use OpenCL to render graphics for display in a QOpenGLWidget. The recommended way to do this seems to be creating a second QOpenGLContext beside the one already present in the QOpenGLWidget, then create a thread where this secondary context can live together with the OpenCL code.
This way Qt can go about it's day as usual with the eventloop running in the main thread. And whenever the QOpenGLWidget decides to paint it will simply fetch data from a buffer prepared in the second thread by the secondary context and the OpenCL inter-op set up there.
This all sounds great on paper, but I am having some problems getting this to work. My question is about how to make the secondary QOpenGLContext "current" in the thread. Because QOpenGLContext::makeCurrent() takes a mandatory QSurface as parameter, and the only surface I have is the one that is available from my QOpenGLWidget, but using that in the secondary thread does not work. I get the following error:
Cannot make QOpenGLContext current in a different thread
So what surface should I use? Or, is there something I missed, or should do differently?
You can create and use a QOffscreenSurface for this purpose.
I have a strange problem rendering OpenGL to QGLWidget from a different thread than the main thread.
There are a lot of official statements from Qt Developers that it is "perfectly possible" to do rendering from a different thread. I followed the explanation in:
http://doc.qt.nokia.com/qq/qq06-glimpsing.html#writingmultithreadedglapplications
I implemented it nearly the same way. The only difference is, that I dont use QWorkspace with different GLWidgets but instead I just create a MainWindow with GLWidget as central widget.
When I start the application, the rendering thread starts rendering frames with a triangle at a random position. After a while (sometimes 2 seconds, sometimes 10 seconds) the thread starts to block on the swapBuffers() call for a very long time. Sometimes swapBuffers() returns spontanously after several seconds. When I move the mouse pointer over the widget or the main window, the swapBuffers returns immediately and the as long as I move the mouse pointer swapBuffers() does not block. After moving the mouse out of the widget or just stop moving the mouse, rendering continues for some seconds and then swapBuffers start blocking again.
I have absolutely no explanation for this behaviour. I am aware that swapBuffers() regulary blocks until a frame is completed and it's also clear to me, that a wait for vsync also might happen during OpenGL buffer swap call. But that should happen in some milliseconds and not block for several seconds. The environment is X11 with GLX.
Does anybody has an idea wtf is going on here?
I dont even have an idea how to find out what the problem might be..
Does anyone tried to implement the rendering from different thread as explained in the document that I linked above?