0
\$\begingroup\$

I'm currently developing a 3D game with its own engine. I have some performance issues in the function that orders all the draw events and sends them to the GPU. I've heard about threads, and I thought that it could be nice if I could pass that code to another thread. The structure is something like:

Global function that receives all events and calls OpenGL to draw

void DrawAll(float dt, vector<draw_events>)

Function inside the EventSystem, that calls the previous function

Events passed by copy, that have pointers to objects in the scene (scene is in main thread)

void ModuleEventSystem::PostUpdate(float dt)
{
   DrawAll(dt, draw_events);
}

This has been tested, and has worked well.

Now I have inside:

std::thread draw_thread;
void ModuleEventSystem::PostUpdate(float dt)
{
   if(draw_thread.IsJoinable())
         draw_thread.Join();

   draw_thread = std::thread(DrawAll, dt, events_to_draw);
}

The problem: not only the performance as become worse, objects in scene doesn't draw at all. I have never worked on threads before, so I don't know if the issue is having a thread with OpenGl calls and pointers pointing to objects in the main thread, or is the bad structure of code... Or even if this kind of optimization is not possible at all. Thank all of you.

\$\endgroup\$
  • 2
    \$\begingroup\$ ...and that is why you just dont "use threads", especially with OGL (do you have proper context?). Same question (with little bit more research than this one) was already asked on SO - you might find it helpful. There is also a bit more general question on same topic here. \$\endgroup\$ – wondra Apr 19 '18 at 18:17
  • 2
    \$\begingroup\$ Unless you have particularly complex calculations to run on your CPU, your bottleneck will be bandwidth in sending data to the GPU- which would probably slow down if you've got multiple threads competing for access to it. \$\endgroup\$ – PixelArtDragon Apr 19 '18 at 20:39
0
\$\begingroup\$

On the note of multithreaded rendering:

General it is inadvisable, due to the GL context being thread specific. In order to render on another thread, you have to make the context current on that particular thread. Besides the cost of making a context current, you should be aware that only one thread can have a current context at any one time, therefore you cannot actually perform parallel rendering. Which leads me to the next point.

On the point of multithreading in general:

There are two main ideas concerning multithreading. The first is the parallel data model, which takes a set of data, and processes it in parallel. This is analogous to your situation, in which you wish to perform some kind of processing on a set of data on multiple threads. This is how a GPU performs rendering, e.g. Each core in the GPU processes the colour of a pixel, or fragment in parallel with other cores. This model is not suitable for your needs, due to the aforementioned problems inherent with OpenGL contexts.

The second method is the parallel task model, which is far more suited to your needs, but would require a non-trivial amount of refactoring to achieve. The basic idea is to separate your tasks into groups, e.g. rendering, physics, game logic etc, and run these tasks on complete data sets in parallel, such as rendering on the main thread, with an additional thread performing physics processing and game logic on yet another thread. In the way, each task only operates on the latest version of the data involved. The benefit of this is that only minimal thread synchronisation is required, which allows maximum performance benefit.

I would recommend reading this post by Intel to understand this more fully.

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

From the OpenGL wiki on OpenGL contexts:

The current context is a thread-local variable, so a single process can have several threads, each of which has its own current context. However, a single context cannot be current in multiple threads at the same time.

Your new rendering thread doesn't have a current OpenGL context selected yet so it renders to nothing and you'll need to release that context from your main app thread before selecting it in your rendering thread.


However if ordering your render call is where the bottleneck is I recommend ditching the multi-threaded sort approach and rather keep all your rendering objects sorted between render calls and insert-sort those that change using, for example, a persistent std::multimap<order_value, draw_event*> container.

Before changing the order value you remove its entry from the multimap and insert it back in the new position after changing the order value.

Rendered objects generally keep their render order between frames.

The fastest sort function is the one you don't call.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.