Multithreaded Rendering on OpenGL

55,312

Solution 1

I have a multithreaded application, in which I'm trying to render with different threads.

DON'T!!!

You will gain nothing from trying to multithread your renderer. Basically you're running into one large race condition and the driver will just be busy synchronizing the threads to somehow make sense of it.

To gain best rendering performance keep all OpenGL operations to only one thread. All parallelization happens for free on the GPU.

Solution 2

I suggest to read the following wiki article from the OpenGL Consortium.

In simple words, it depends a lot on what you mean for multi threading in regards to OpenGl, if you have one thread doing the rendering part and one (or more) doing other jobs (i.e. AI, Physics, game logic etc) it is a perfectly right.

If you wish to have multiple threads messing up with OpenGL, you cannot, or better, you could but it will really give you more troubles than advantages.

Try to read the following FAQ on parallel OpenGL usage to have a better idea on this concept:

http://www.equalizergraphics.com/documentation/parallelOpenGLFAQ.html

Solution 3

In some cases it may make sense to use multiple rendering contexts in different threads. I have used such a design to load image data from filesystem and push this data into a texture.

Solution 4

OpenGL on Mac OS X is single-thread safe; to enable it:

#include <OpenGL/OpenGL.h>

CGLError err = 0;
CGLContextObj ctx = CGLGetCurrentContext();

// Enable the multi-threading
err =  CGLEnable( ctx, kCGLCEMPEngine);

if (err != kCGLNoError ) {
     // Multi-threaded execution is possibly not available
     // Insert your code to take appropriate action
}

See: Concurrency and OpenGL - Apple Developer

And: Technical Q&A QA1612: OpenGL ES multithreading and ...

Share:
55,312

Related videos on Youtube

filipehd
Author by

filipehd

Updated on September 20, 2021

Comments

  • filipehd
    filipehd almost 3 years

    I have a multithreaded application, in which I'm trying to render with different threads. First I tried to use the same Rendering Context between all threads, but I was getting NULL current contexts for other threads. I've read on the internet that one context can only be current at one thread at a time.

    So I decided to make something different. I create a window, I get the HDC from it and create the first RC. AFter that, I share this HDC between threads, and in every new thread I create I obtain a new RC from the same HDC and I make it current for that thread. Everytime I do it, the RC returned is always different (usually the previous value + 1). I make an assertion to check if wglGetCurrentContext() returns a RC, and it looks like it returns the one that was just created. But after making the rendering, i get no rendering and if I call GetLastError() I obtain error 6 (invalid handle??)

    So, does this mean that, despite every new call of wglCreateContext() gives me a new value, somehow it means that all these different values are the same "Connection channel" to the OpenGL calls?

    Does this mean that I will always have to invalid the previous Rendering Context on a thread, and activate it on the new one? I really have to make this sync all the time or is there any other way to work arround this problem?

    • Vitaly Dyatlov
      Vitaly Dyatlov about 12 years
    • filipehd
      filipehd about 12 years
      well I'm doing something really similiar to that, but I'm getting Error 6 after wglShareLists call
    • RelativeGames
      RelativeGames about 11 years
      I think the question should also read as "Since multithreaded DirectX 11 rendering is faster than single threaded, can the driver bring the same improvements from OpenGL calls as well ?"
  • filipehd
    filipehd about 12 years
    Ok I didn't express myself correctly. Doesn't mean they will be both rendering on screen. Actually they won't. One thread draws on the backbuffer, and copies its contents to a texture. The other one simply "Blits" the Texture on screen. It means the worker thread can render on backbuffer other complex stuff.
  • Hannesh
    Hannesh about 12 years
    "One thread draws on the backbuffer, and copies its contents to a texture. The other one simply "Blits" the Texture on screen." Whyy? Shouldn't you just be swapping the back and front buffers when you want to present?
  • datenwolf
    datenwolf about 12 years
    @filipehd: Even then using multiple threads doesn't make sense. The "blitter" thread would have to wait for the "renderer" thread to finish. The whole point of multithreading is to keep things working simultanously; which you don't.
  • filipehd
    filipehd about 12 years
    @datenwolf Even though you guys are right, it doesn't answer to my question of why I do get error 6 after making wglShareLists(). Still, it means that in order to do what I want I need auxiliar buffers like FBO or PBuffer?
  • filipehd
    filipehd about 12 years
    @Hannesh No, because I am not swapping the buffers, but just copying to the texture the very small part of the screen that was changed. So if i change a square 10x10 px, I am only copying this portion to the texture, and then rendering the texture on screen. No need to REDRAW the whole scene again
  • datenwolf
    datenwolf about 12 years
    @filipehd: Why are you creating a new context for each new thread anyway? RCs are not bound to the context they were created with. | Why are you constantly creating new threads? Have only a single thread and send it work queues to process. Google for "thread pooling"
  • filipehd
    filipehd about 12 years
    @datenwolf because I've been reading in several places that sharing the same RC between threads is not a good practice and not safe. So that's why I decided to create an RC for each thread.
  • filipehd
    filipehd about 12 years
    but that's exactly the idea of my application - Different threads take care of different logic - What I can't understand is why am I getting error 6 after calling wglShareLists. Maybe I am calling this fuction too late? I'll try to use the idea written on the first link you gave me to check how it works.
  • datenwolf
    datenwolf about 12 years
    @filipehd: Well, sharing (i.e. having the context being active in multiple threads at the same time) a context is not possible, but passing it from thread to thread is possible. However: You should avoid re-creating threads and contexts all the time. Create a thread pool exactly one time and stick to it. Same for contexts, though I strongly advise against using multiple contexts over several thread. Have one dedicated OpenGL thread, things get much easier, and also more performant that way.
  • datenwolf
    datenwolf about 12 years
    @filipehd: Different logic means, different subsystems. One thread for networking, one for user input, one for everything OpenGL, etc. Really, don't try to spread graphics output over multiple threads. It's a huge mess. Also the "blitting" operation (drawing a textured quad), that the whole overhead of switching to another thread will cause more time consumed, that the actual "blitting". Also the GPU is a exclusive resource, you can't have multiple threads use the GPU at the very same time, so the blitter thread would have to wait for any other operation on the GPU to finish anyway.
  • Luca
    Luca about 12 years
    At the end, it's just like running multiple OpenGL applications on the same GPU. Not so bad as it seems in your answer: isn't the driver able to pipeline multiple context operations?
  • datenwolf
    datenwolf about 12 years
    @Luca: With the fine distinction, that separate processes usually render to separate DCs. However OP has multiple threads juggle around data from a common RC. Yes, it can be done, but why bother?
  • filipehd
    filipehd about 12 years
    @datenwolf i dont know about this, but so you telling me that, for example, in a complex game, every single pixel of the rendered scene is always rendered on the SAME thread? They never use multiple threads to improve rendering performance?
  • datenwolf
    datenwolf about 12 years
    @filipehd: This is indeed the case. Why would you try to multithread the rendering commands anyway? The parallelization happens on the GPU, which takes a large batch of geometric primitives, processes them in parallel (vertex shader), determines the covered fragments and processes the fragments in parallel (fragment shader).
  • datenwolf
    datenwolf about 12 years
    @filipehd: Now think about what would happen if you actually sent two batches of geometric primitives to the GPU in parallel: You would end up in a race condition, because they both would (very likely) touch the same fragments. This is a race condition which can only be resolved by synchronizing the two batches, which stalls the processing pipeline, hence causing a severe performance hit.
  • datenwolf
    datenwolf about 12 years
    @filipehd: Also every major game engine in existance will do everything it can, to keep all renderer operations not only in one thread, but also on the same CPU core. Doing everything in only one thread is to avoid driver bugs (I know of no OpenGL driver at all, that does not have some sort of multithreading issues; only recently NVidia had some). And keeping things on one core is to improve cache locality.
  • filipehd
    filipehd about 12 years
    @datenwolf thank you very much for the information. Makes 100% sense what you are saying.
  • Tara
    Tara over 10 years
    It would be better to load the textures (all non-OpenGL stuff) in another thread and then update the texture in the thread which owns the context.
  • JoR
    JoR over 10 years
    Do you think context sharing slows down the GL driver? Updating textures is one of the functions that consume most of the time, so imho doing this in separate thread saves a lot of time in the main thread. Of course that is only valid for data that is not needed immediately.
  • Tara
    Tara over 10 years
    I also implemented texture streaming once. Context sharing is really bad if you use the context from multiple threads at the same time. But I'm not how fast it is when you can guarantee that you NEVER use it concurrently. I don't think it would be slow. But if it works, then it works. Btw: If you need asynchronous texture upload, use PBOs.
  • JoR
    JoR over 10 years
    I agree, when trying that on different platforms the performance was not good on all of them. I agree also that it is only suitable for data that is prefetched in background and not used in active rendering thread.
  • Tara
    Tara over 10 years
    I see... When I implemented texture streaming (using precomputed mip maps), which streamed thousands of textures. I used PBOs for that. The performance was pretty good. All the data was loaded in a second thread, but uploaded in the renderer thread. I would go with something like that.
  • Cedric
    Cedric over 7 years
    @datenwolf I'm looking for something similar to OP. Basically, one thread is used for anything UI related, which needs to have a constant time behaviour (for responsive UI) and one thread for image processing and computation. It is just WAY easier to pass a texture ref (which is a GLuint) than to make sure you're not accessing the image data in the UI thread while it is written by the image processing thread, which would cause funky images to be rendered.
  • Soonts
    Soonts almost 7 years
    The question is about Windows. Not listed in tags, but HDC and GetLastError make it obvious.
  • geowar
    geowar almost 7 years
    If you're a Windows person… I'm not… Plus OS specific extensions often make it into the non-OS specific GL release.
  • geowar
    geowar almost 7 years
    I've worked on several games where multiple renderers were running on multiple threads. For example a racing game where the external forward, rear-view (for the mirrors) and HUD were all drawn on their own threads and then composited into the view presented to the player. This is not an unrealistic thing to do.
  • datenwolf
    datenwolf almost 7 years
    @geowar: What graphics API did these games use? OpenGL used to have (still has) a certain reputation of being finicky when it comes to multithreading. If you've done everything right, then yes, several rendering threads may hit the fast path. But it takes a lot of profiling to get this right.
  • Hi-Angel
    Hi-Angel over 6 years
    FAQ link is an empty page.
  • Makogan
    Makogan about 6 years
    "keep all OpenGL operations to only one thread" This is not entirely true, you should keep all RENDERING operations to one thread. Although it is true that no benefit comes in terms of execution, there are reasons to have buffer functions running in other threads for code structure. For example a producer thread creating and stroring data in VBOs to be rendered. Not using the GL functions in the producer thread leads to more complicated code.
  • datenwolf
    datenwolf about 6 years
    @Makogan: I know what you think of, BT;DT. While it should work in theory (and does most of the time), it's prone to exposing nasty driver bugs – in fact right now I'm debugging an application which does some heavy inter-thread buffer updates, also via compute, and the way textures are dynamically resized triggers a bug the locks up the driver hard (have to reboot to fix it). For what's it worth if you want to update OpenGL buffers from a separate thread, create and map the buffer objects in the GL thread, then send the worker message at which address to write to.