4
\$\begingroup\$

The most basic game loop is like this :

while(1)
{
   update();
   draw();    
   swapbuffers();
}

This is very simple but have a problem : some drawing commands can be blocking and cpu will wait while he could do other things (like processing next update() call).

Another possible solution i have in mind would be to use two threads :

one for updating and preparing commands to be sent to gpu, and one for sending these commands to the gpu :

//first thread
while(1)
{
   update();
   render(); // use gamestate to generate all needed triangles and commands for gpu 
             // put them in a buffer, no command is send to gpu
             // two buffers will be used, see below
   pulse(); //signal the other thread data is ready
}

//second thread
while(1)
{
   wait();              // wait for second thread for data to come
   send_data_togpu();   // send prepared commands from buffer to graphic card
   swapbuffers();      
}

also : two buffers would be used, so one buffer could be filled with gpu commands while the other would be processed by gpu.

Do you thing such a solution would be effective ? What would be advantages and disadvantages of such a solution (especially against a simpler solution (eg : single threaded with triple buffering enabled) ?


EDIT : I have taken a real example to show a situation where there would be an advantage :

Let's say i have a scene which consist of rendering same entity a lot of times (like in a RTS) :

in this example :

  • updating world take 100 ms (because there is lot of entities)
  • sending commands to gpu almost nothing (only calls to gldrawelements for the same entity, vertex buffers only sent once) (almost 1ms)
  • scene rendering by gpu is also heavy, take 100 ms

total = ~200 ms (or 5 fps)

I dont think gpu could be draw something during update procedure because gpu doesnt have any idea when scene start (and cant assume glclear = beginning of scene rendering)


now lets say the system is using two independant threads :

  • update take 100ms
  • sending cmds take 1ms
  • next update can already start no need to wait for gpu
  • scene rendering take 100ms

total = ~100 ms (actually a little more than this because of synchronisation)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ There's actually no need to have two CPU threads to get the second scenario described in your edit. As I mentioned, the driver will already double-buffer the GPU commands behind the scenes. As long as you don't do anything that explicitly forces the CPU and GPU to wait for each other, you should get scenario 2 even with just one CPU thread. The boundary of one frame and the next is defined by the SwapBuffers call, so the driver has no problem identifying that. \$\endgroup\$ Commented Jul 7, 2012 at 18:16

2 Answers 2

5
\$\begingroup\$

The fact that multithreaded rendering is a major feature in D3D11 suggests that it indeed can be useful to have multiple threads for generating the GPU command stream. :) Indeed the CPU is "just" storing a sequence of commands for the GPU to execute later, but that doesn't mean it's a trivial amount of work.

Incidentally, with an API like D3D or OpenGL it's mostly not necessary to explicitly double-buffer things yourself. The driver is already internally double-buffering the command streams (or it's using a ring buffer or some such).

Besides, rendering the scene usually also involves doing a bunch of CPU work like updating scene graph node transforms, frustum/occlusion culling, LOD selection, and so on beyond solely queuing up GPU commands. As game worlds become more complex it starts making a lot of sense to multithread all this stuff.

\$\endgroup\$
3
\$\begingroup\$

I can't authoritatively say that this wouldn't work, but it seems unlikely to be of benefit as the GPU is already a separate piece of hardware that runs in parallel with the CPU. The data just gets piped down to it as appropriate and even the final display call is often buffered up in my experience rather than being a blocking call. Therefore it would be surprising if any significant rendering commands held up the CPU to the extent where threading would help you.

\$\endgroup\$
3
  • \$\begingroup\$ Would it be possible for the graphic card to be still rendering some stuff even after a glswapbuffers() call ? (in this case the buffer swap would happen asynchronously after call) if this is true : 1) how can i be sure at some point in code that everything i submitted is done ? 2) how many "arrays of commands" can graphic card hold before it will finally block cpu (driver defendant or only one)? \$\endgroup\$
    – tigrou
    Commented Jul 7, 2012 at 14:23
  • \$\begingroup\$ @tigrou (1) call glFinish. (2) it's driver-dependent. \$\endgroup\$ Commented Jul 7, 2012 at 18:17
  • \$\begingroup\$ @tigrou: SwapBuffers() usually blocks if you're calling it when the previous frame hasn't finished rendering yet. So that means you can have 1 frame of latency at most. But this behaviour can differ, depending on wether you have double buffering, tripple buffering or V-Sync enabled. If the previous rendering is finished before calling SwapBuffers(), the function should not stall at all. \$\endgroup\$
    – Tara
    Commented May 25, 2014 at 13:24

You must log in to answer this question.

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