Multithreaded Rendering on Opengl

Multithreaded Rendering on OpenGL

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.

Is OpenGL multithreaded?

If depends on what you mean by "multithreaded".

If you are thinking about C++ feature like (sharing memory, using locks, etc), then no, OpenGL does not work that way. But this doesn't mean than you can not use threads. You can, with special care.

The main thing to be aware of is the context. You can have several contexts. You can set as current any context for any thread, but only one can be set as current for a thread, not two contexts for the same thread.

Using shared contexts gives you a bit of advantage with multithreading. They share some resources like textures and VBOs. For example, you set ctx1 as current for a thread and use gl-commands in that thread to update a texture to the GPU. Once the update is finished that texture is available for the shared context ctx2 set as current in other thread. The OGL wiki tells about this here and here.

Being that said, the question is "why do I need mutlthreading?" The common answer is "to make things happen faster". The point is that the GPU will draw step by step (using all of its parallelism, of course) but will NOT process two draw commands at the same time. Also, setting a context as current has a light perfomance penalty.

What you likely are looking for is sending data to GPU while it's rendering. You can use shared contexts as I wrote before. But there are other technics like streaming, you can read more at OpenGL Insights book Chapter 28 "Asynchronous Buffer Transfers".

Creating OpenGL structures in a multithreaded program?

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.

  1. 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();
  2. 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.

Multithreaded object loading while rendering with OpenGL

The easiest way to handle this is to have the main thread create and manage all of the OpenGL objects, while the loading thread does the File IO (easily the slowest part of the loading). Once the loading thread is finished with loading a particular asset, it can deliver the loaded data to the main thread via <insert your favorite thread-safe mechanism here>, which can do the final OpenGL uploading part.

After all, it's not like rendering a loading screen is a huge performance drain or something, so the cost of uploading on the main thread will be minimal. This also permits you to do that loading bar thing, since your main thread will frequently be getting the results of the loading process, so it knows at any time how much of the loading is finished.

If you absolutely must have two threads both making OpenGL calls for some reason, then you should also have two OpenGL contexts, each being current in a different thread, and the two contexts sharing objects with each other. GLFW is perfectly happy to provide this if you ask it nicely. Create your main window as normal, then set the GLFW_VISIBLE hint to GLFW_FALSE, and create a second window (with an arbitrary resolution). You should pass the main window as the last parameter to glfwCreateWindow, so that the two contexts can share objects. You can then set each window current in different contexts and you're fine.

One word of caution. Contexts that share objects between them only share certain objects. Objects which reference other objects cannot be shared (also query objects are unsharable for some reason). VAOs reference buffer objects, so they can't be shared. So there's no point in trying to create them on the off-screen context.

Multi-threaded rendering D3D/OpenGL/Whatever

Your question comes from a misunderstanding of the difference between "make their renderers multi-threaded" and "multithreaded rendering".

A "renderer", or more precisely a "rendering system," does more than just issue rendering commands to the API. It has to shuffle memory around. It may have to load textures dynamically into and out-of graphics memory. It may have to read data back after some rendering process. And so forth.

To make a renderer multithreaded means exactly that: to make the rendering system make use of multiple threads. This could be threading scene graph management tasks like building the list of objects to render (frustum culling, BSPs, portals, etc). This could be having a thread dedicated to texture storage management, which swaps textures in and out as needed, loading from disk and such. This could be as in the D3D11 case of command lists, where you build a series of rendering commands in parallel with other tasks.

The process of rendering, the submission of actual rendering commands to the API, is not threaded. You generally have one thread who is responsible for the basic glDraw* or ::DrawIndexedPrimitive work. D3D11 command lists allow you to build sequences of these commands, but they are not executed in parallel with other rendering commands. It is the rendering thread and the main context that is responsible for actually issuing the command list; the command list is just there to make putting that list together more thread-friendly.

OpenGL Rendering in a secondary thread

As long as the OpenGL context is touched from only one thread at a time, you should not run into any problems. You said even your single threaded program made your system sluggish. Does that mean the whole system or only your own application? The worst that should happen in a single threaded OpenGL program is, that processing user inputs for that one program gets laggy but the rest of the system is not affected.

If you use some compositing window manager (Compiz, KDE4 kwin), please try out what happens if you disable all compositing effects.

When you say X errors do you mean client side errors, or errors reported in the X server log? The latter case should not happen, because any kind of kind of malformed X command stream the X server must be able to cope with and at most emit a warning. If it (the X server) crashes this is a bug and should reported to X.org.

If your program crashes, then there's something wrong in its interaction with X; in that case please provide us with the error output in its variations.

OpenGL multithreading

First, a warning. Your "slight problem" is not slight at all. It is race condition, which is undefined behavior in C++, which, in turn, implies that anything could happen, including:

  • Everything renders fine

  • Image flickers

  • Nothing renders at all

  • It crashes on the last Saturday of every month. Or working fine on your computer and crashing on everyone's else.

Seriously, do not ever rely on UB, especially when writing library/framework/game engine.

Now about your question.

Lets leave aside any practical benefits of your approach and fix it first.

Actually, OpenGL implementation uses something very similar under the hood. Commands are executed asynchronously by the driver thread. I recommend you to read about their implementation to get some ideas on how to improve your design.

What you need to do, is to somehow "capture" the state at the time you post a rendering command. Simplest possible thing - copy the CORE_RENDERER->state into closure and use this copy to do the rendering. If state is large enough, it can be costly, though.

Alternative solution (and OpenGL goes that way) is to make every change in the state a command also, so

CORE_RENDERER->state.ModelMatrix = (*it).matrix;
CORE_RENDERER->state.ActiveSubmesh = (*it).submesh;

translates into

Matrix matrix = (*it).matrix;
Submesh submesh = (*it).submesh;

THREAD_POOL->service.post([&,matrix,submesh]{
CORE_RENDERER->state.ModelMatrix = matrix;
CORE_RENDERER->state.ActiveSubmesh = submesh;
});

Notice, however, that now you can't simply read CORE_RENDERER->state.ModelMatrix from your main thread, as it is changing in a different thread. You must first ensure that command queue is empty.



Related Topics



Leave a reply



Submit