1
\$\begingroup\$

I understand OpenGL is not thread-safe, but at the same time, Google seems to be giving me mixed responses.


I have a LibGDX game that functions normally, UI/rendering/logic on the same thread, everything is happy.

However, at a certain point, the user can initiate a process which involves a progress bar and lots of slow rendering (to off-screen FBOs). Right now I'm doing this on that one single thread, resulting in locking-up of the UI. I currently break out of the loop periodically in order release the lock on the UI every now and then.

What I'd like to happen is for another thread to be created at this point, and for all of the slow rendering to happen on it while the main UI/rendering/logic thread just displays a progress bar (without locking up of course).


Since both threads will be using OpenGL at the same time, is this possible?

\$\endgroup\$

4 Answers 4

2
\$\begingroup\$

An OpenGL context may be current on at most one thread at a time. If you make a context current in one thread, it will be made uncurrent in the previous thread. So while you can migrate contexts between threads, you can not do so in any concurrent manner.

While there is the existence of shared contexts and shared resources, those are not exactly in the fast path and you'd need to do some work to get them set up, if at all available.

\$\endgroup\$
2
\$\begingroup\$

tl;dr: NO.

Long answer:
As @LarsViklund pointed out, you will have significant trouble using multiple threads to render. OpenGl is kinda bad at multithreading, so don't.
If your rendering is slow it's not because of slow OpenGL calls. Your architecture is just bad. You can improve it by moving away all your logic (UI events, physics, AI,...) from your rendering thread. That way, there will be more frequent updates and your UI will not lock up. Also the overall experience wil be a lot smoother. There are many models for multithreading.
Maybe also take a look at my answer about multithreading: Multithreading in Gamedevelopment, specifically Networking

\$\endgroup\$
1
\$\begingroup\$

Alternatively, you can break up your long, slow render parts to intersperse with your UI updates.

Slow part takes too long? Break it into multiple draws. Each draw is still too long? Fill a smaller area.

It might be a little bit inside out, but it's a way to do it all in one thread.

\$\endgroup\$
1
\$\begingroup\$

My understanding is that the goal is to have a background "high quality render" while still updating an on-screen progress bar and eventually producing a still image from the background process.

The easiest way to do this will probably involve having two separate OpenGL contexts, each set as 'current' on one thread.

The first one renders your progress bar (or whatever else is needed) to the main screen, from your UI thread, as normal.

The second context handles your lengthy rendering process from a background thread, and renders into an FBO.

These may or may not be shared contexts. If they're shared contexts, they can share model data (ie: the second context can render using VBOs, textures, etc. created in the first context, which may be useful depending on your application), but this will make rendering slower in both contexts, primarily due to driver-imposed memory fences around that shared data. On the other hand, you'll be able to convert the rendered image directly into a texture that will be accessible to the first context, without pulling the data back to the CPU.

If they're not shared contexts then rendering will be somewhat quicker, but you'll need to separately load any required model data into the second context, and you'll need to pull the final rendered image back to the CPU and then explicitly load it into the first context as a texture, so that it can be used from there.

To get the final rendered image to the screen using shared contexts, you just need to either convert the FBO contents into a glTexture; that glTexture will be available from the first context (once synch has completed between the two contexts).

If not using shared contexts, then use glReadPixels() to get the rendered pixels back out of the second context, and then use those pixels to create a texture inside the first context.

Either way, you're left with a texture of the rendered scene that you can use from within the first rendering context; the one that's rendering to the main screen.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .