Are multiple threads rendering to individual OpenGL contexts thread safe? - c++

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.

Related

How to load mesh in new thread

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.

OpenGL asynchronous render of scene that takes several seconds

I've implemented a Mandelbrot fractal generator using wxWidgets and OpenGL. The computation is performed inside a fragment shader, and I'm using a wxGLCanvas widget to display the result. It's working well, but when I want to do a high-res export, the thread locks up for a few seconds which freezes the UI.
Initially I tried moving all rendering code (and context creation) into a separate render thread, but what I found was that it wasn't just the render thread that would lock up, but ALL threads. This could be easily demonstrated by spawning a new thread prior to doing the render that just prints a message to stdout in a loop. It would get as far as printing 1 message before freezing, then resuming once the render was complete.
To perform the file export, I first render to a texture, then I read the pixels into main memory with glGetTexImage. The render occurs asynchronously as you would expect, but the glGetTexImage function will block (again, that's expected). I therefore tried using glFenceSync in combination with glGetSynciv to only call glGetTexImage once the fence had been reached indicating a completed render.
I could confirm the draw call was returning immediately, but the moment I returned to the wxWidgets event loop to wait for the render to finish, all threads in the application would freeze. I figure maybe wxWidgets is making an OpenGL call that's forcing a sync (prob something in wxGLCanvas) - I'm fairly sure it wasn't something in my code.
I'm not sure why all threads were blocking on the glGetTexImage call, rather than just the render thread. I thought it might be a quirk of my setup (hardware, driver, OS, etc.), but got the same result on a completely different platform.
The only remaining option I can think of is to do the export render in another process with its own OpenGL context. If I'm still to use wxGLCanvas to set up the context I would probably need a visible window on the screen, which isn't ideal. Also, if the user tries to close the window during the render, it would be unresponsive.
Any ideas?
Not so much a solution, but a workaround (suggested by derhass in the comments) is to split the render into several smaller renders. I'm drawing the image in horizontal strips, each 10 pixels high, and calling wxSafeYield() between each one so the UI remains somewhat responsive (and the user can abort the render if it's taking too long).
An additional advantage is that there's less danger of overloading the GPU. Prior to implementing this I once kicked off a long render that caused my displays to shut off and I had to do a hard reboot.

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.
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).

How to make QOpenGLContext current without surface in Qt5?

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.

using qglcontext from other threads

Is there a way to use the qglcontext of the glwidget from other threads. Because I need to do some texture uploading from other threads. However after the texture upload or even during it context must be also in the service of my rendering glwidget. Is there a documentation or a solid (assumption free) answer for this?
OpenGL does not support multithreaded rendering, all OpenGL calls must be performed from the thread where context was created. But if you whant to just load textures, you may load it from other threads, than post the results to that thread from wich OpenGL context was created for example to glTexImage2D, as image info. To do so must be add some thread management (signals e.t.c...).
For more information look at Concurrency and OpenGL.
also QGLWidget multithreaded example?.
To work from other threads you must create separate contexts with them or perform some sharing context management.
From official Qt documentation:
As of Qt version 4.8, support for doing threaded GL rendering has been improved. There are three scenarios that we currently support:
Buffer swapping in a thread.
Swapping buffers in a double buffered context may be a synchronous, locking call that may be a costly operation in some GL implementations. Especially so on embedded devices. It's not optimal to have the CPU idling while the GPU is doing a buffer swap. In those cases it is possible to do the rendering in the main thread and do the actual buffer swap in a separate thread. This can be done with the following steps:
Call doneCurrent() in the main thread when the rendering is finished.
Call QGLContext::moveToThread(swapThread) to transfer ownership of the context to the swapping thread.
Notify the swapping thread that it can grab the context.
Make the rendering context current in the swapping thread with makeCurrent() and then call swapBuffers().
Call doneCurrent() in the swapping thread.
Call QGLContext::moveToThread(qApp->thread()) and notify the main thread that swapping is done.
Doing this will free up the main thread so that it can continue with, for example, handling UI events or network requests. Even if there is a context swap involved, it may be preferable compared to having the main thread wait while the GPU finishes the swap operation. Note that this is highly implementation dependent.
Texture uploading in a thread.
Doing texture uploads in a thread may be very useful for applications handling large amounts of images that needs to be displayed, like for instance a photo gallery application. This is supported in Qt through the existing bindTexture() API. A simple way of doing this is to create two sharing QGLWidgets. One is made current in the main GUI thread, while the other is made current in the texture upload thread. The widget in the uploading thread is never shown, it is only used for sharing textures with the main thread. For each texture that is bound via bindTexture(), notify the main thread so that it can start using the texture.
Using QPainter to draw into a QGLWidget in a thread.
In Qt 4.8, it is possible to draw into a QGLWidget using a QPainter in a separate thread. Note that this is also possible for QGLPixelBuffers and QGLFramebufferObjects. Since this is only supported in the GL 2 paint engine, OpenGL 2.0 or OpenGL ES 2.0 is required.
QGLWidgets can only be created in the main GUI thread. This means a call to doneCurrent() is necessary to release the GL context from the main thread, before the widget can be drawn into by another thread. You then need to call QGLContext::moveToThread() to transfer ownership of the context to the thread in which you want to make it current. Also, the main GUI thread will dispatch resize and paint events to a QGLWidget when the widget is resized, or parts of it becomes exposed or needs redrawing. It is therefore necessary to handle those events because the default implementations inside QGLWidget will try to make the QGLWidget's context current, which again will interfere with any threads rendering into the widget. Reimplement QGLWidget::paintEvent() and QGLWidget::resizeEvent() to notify the rendering thread that a resize or update is necessary, and be careful not to call the base class implementation. If you are rendering an animation, it might not be necessary to handle the paint event at all since the rendering thread is doing regular updates. Then it would be enough to reimplement QGLWidget::paintEvent() to do nothing.
As a general rule when doing threaded rendering: be aware that binding and releasing contexts in different threads have to be synchronized by the user. A GL rendering context can only be current in one thread at any time. If you try to open a QPainter on a QGLWidget and the widget's rendering context is current in another thread, it will fail.
In addition to this, rendering using raw GL calls in a separate thread is supported.