views:

310

answers:

3

I was just reading through the DirectX documentation and encountered something interesting in the page for IDirect3DDevice9::BeginScene :

To enable maximal parallelism between the CPU and the graphics accelerator, it is advantageous to call IDirect3DDevice9::EndScene as far ahead of calling present as possible.

I've been accustomed to writing my game loop to handle input and such, then draw. Do I have it backwards? Maybe the game loop should be more like this: (semi-pseudocode, obviously)

while(running) {
    d3ddev->Clear(...);
    d3ddev->BeginScene();
    // draw things
    d3ddev->EndScene();

    // handle input
    // do any other processing
    // play sounds, etc.

    d3ddev->Present(NULL, NULL, NULL, NULL);
}

According to that sentence of the documentation, this loop would "enable maximal parallelism".

Is this commonly done? Are there any downsides to ordering the game loop like this? I see no real problem with it after the first iteration... And I know the best way to know the actual speed increase of something like this is to actually benchmark it, but has anyone else already tried this and can you attest to any actual speed increase?

+1  A: 

The short answer is yes, this is how it's commonly done. Take a look at the following presentation on the game loop in God of War III on the PS3:

http://www.tilander.org/aurora/comp/gdc2009_Tilander_Filippov_SPU.pdf

If you're running a double buffered game at 30 fps, the input lag will be 1 / 30 ~= 0.033 seconds which is way to small to be detected by a human (for comparison, any reaction time under 0.1 seconds on 100 metres is considered to be a false start).

Andreas Brinck
@Andreas: 3msec is definitely not way below detectable for a human, when playing midi-piano anything above 4-5msec is noticable and anything above 10msec is, if not unplayable, very annoying. Might be a difference between playing a musical instrument\video game though.
Viktor Sehr
A: 

Its worth noting that on nearly all PC hardware BeginScene and EndScene do nothing. In fact the driver buffers up all the draw commands and then when you call present it may not even begin drawing. They commonly buffer up several frames of draw commands to smooth out frame rate. Usually the driver does things based around the present call.

This can cause input lag when frame rate isn't particularly high.

I'd wager if you did your rendering immediately before the present you'd notice no difference to the loop you give above. Of course on some odd bits of hardware this may then cause issues so, in general, you are best off looping as you suggest above.

Goz
+1  A: 

Since I always felt that it was "awkward" to draw-before-sim, I tended to push the draws until after the update but also after the "present" call. E.g.

while True:
   Simulate()
   FlipBuffers()
   Render()

While on the first frame you're flipping nothing (and you need to set up things so that the first flip does indeed flip to a known state), this always struck me as a bit nicer than putting the Render() first, even though the order of operations are the same once you're under way.

dash-tom-bang