DirectDraw:
Flipping Chains
By: Jack Hoxley
Written: May 2000
You will only have come across
flipping very briefly, as it only takes one line of code in a directdraw application.
Despite it being quite a small piece (code wise) it is actually one of the most
important parts of a directdraw application. Flipping is where it copies everything
you've just drawn on the backbuffer to the primary buffer (and therefore appearing
on screen). There are several things that you can change to adjust how the flipping
process works.
First off you must understand
what happens when you flip the backbuffer. Normally you're application will
be double buffered, only windowed mode can be run single-buffered. Double buffered
means that there are two surfaces, the primary and the backbuffer; Every time
you flip the surfaces DirectDraw swaps the memory round, the backbuffers data
appears on the primary buffer and the primary buffer's data appears on the back
buffer:
If you think of A and
B as being two images - A being blank (nothing) and B being
a proper picture. In stage 1, the primary buffer is A and the Backbuffer
is drawn to, therefore it will now have an image in it (B). The second
stage sees the image on the backbuffer flipped to the primary buffer, and we
would now be seeing the picture (B), and at the same time the backbuffer
would now be empty (A). The third sees the cycle begining again, this
time the primary buffer has what was just composed on the backbuffer (in step
2) and the back buffer has what was just displayed on screen (in step 2).
In real terms, as far as you're
concerned, whilst the back buffer is being flipped to the primary buffer you're
application is busy. It is not doing anything - you've sent your message to
DirectDraw, and whilst it's doing it your application is idle. In an ideal world
you'd want to get straight on with drawing the next frame, so that as soon as
the current frame has been flipped you can flip again. One particular way of
doing this is called tripple buffering, more later on this.
You may have noticed that there
is a flag at the end of the flip statement - this can be changed to suit you.
This table summarises what they all do, and if their useful to you.
Flag |
Meaning |
DDFLIP_DONOTWAIT
|
If you want
to keep drawing whilst the flip is pending, use this flag. This doesn't
offer any great speed advantage. |
DDFLIP_EVEN
|
This is used
when displaying video on an overlay surface. It will not be of any use
normally. |
DDFLIP_INTERFVAL2
|
DirectDraw
flips on the second vertical refresh of the screen. |
DDFLIP_INTERFVAL3
|
DirectDraw
flips on the third vertical refresh |
DDFLIP_INTERFVAL4
|
Directdraw
flips on the fourth vertical refresh |
DDFLIP_NOVSYNC
|
Allows DirectDraw
to flip faster than the monitors refresh rate, but may introduce video
artifacts |
DDFLIP_ODD
|
This is similiar
to the DDFLIP_EVEN flag, and is only used when displaying video on overlay
surfaces. |
DDFLIP_WAIT
|
This is the
default flag. This forces DirectDraw to wait until the flipper is free
if it is busy. This is rock-solid, and will not miss a single frame -
but may well slow down because of this. |
DDFLIP_STEREO
|
When this
is set, the primary buffer becomes the main stereo buffer, and the backbuffers
are the Left and Right stereo surfaces. The hardware then flips between
the left and right on every screen refresh. This is hardware dependent. |
You may well find that some of
the above flags improve performance, but it is unlikely that they will produce
an significant speed increase.
Tripple Buffering
As mentioned earlier this is the best way of increasing speed. The good thing
is that it's not very hardware dependant and that it is incredibly easy to implement.
Whereas before you will have used two buffers, a primary buffer and a back buffer;
when using tripple buffering you have a primary buffer and two back buffers.
Normally you have to wait for
the flip to be completed before you can start working on the next frame; tripple
buffering allows you to start working on the next frame whilst DirectDraw is
still flipping the previous frame. Because of this you can achieve a sometimes
dramatic speed increase, on my main computer it goes from 60fps to 84fps, and
it isn't a top-of-the-range 3D card either (Savage4 Pro).
The two things you have to bare
in mind are memory and number of buffers. You may well be fooled into thinking
that if three buffers are faster than two, four buffers will be faster than
three. Not true. Using any more than three buffers in the flipping chain will
do the opposite and start to slow your program down. This is down to memory.
If a 640x480 buffer takes up 320kb (roughly), then 2 surfaces will take up 640kb
(the normal chain) and three buffers will take up 960kb. The more you add, the
more memory you will need; and the more memory you use up the slower things
will get; it's fairly obvious when you think about it.
Tripple buffering can be visualised
as shown in these two diagrams:
This first picture
is a normal flipping chain, showing the backbuffer linking to the primary buffer
This second picture
shows two backbuffers linked seperately to the primary buffer.
Whilst Buffer 1
is busy, buffer 2 is not busy; and therefore can be blitted to. This is better
shown in the following diagram:
This pattern will
keep repeating itself, so only three frames are shown. The flipping sequence
can also be demonstrated by this following diagram:
A |
B |
C |
{- |
-- |
-- |
B |
C |
A |
C |
A |
B |
For every frame
the current image is moved along one.
So, How do I do
this?
Hopefully now you understand how it works; the code is very simple, and is shown
below:
'First
you create the primary, specifying 2 attached surfaces
ddsd1.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
ddsd1.lBackBufferCount = 2 'This would normally be
'1'
Set primary = dd.CreateSurface(ddsd1)
'Then we retrieve pointers to the backbuffer. You
only need one;
'as directdraw sorts out which one you're drawing to - so you always
'Draw to the same object.
Dim caps As DDSCAPS2
caps.lCaps = DDSCAPS_BACKBUFFER
Set backbuffer = primary.GetAttachedSurface(caps)
backbuffer.GetSurfaceDesc ddsd3 |
|
|
|
Simple! It would
be advised to let the user decide which mode they want - A card with little
memory will not be able to use tripple buffering to it's advantage (it would
slow down rather than speed up). If you wanted to automate this you could check
the amount of available video memory (see Enumeration)
and then make a decision based on that information.
The Retained
Mode program has a facility to use Tripple buffering, and it is quite obvious
the speed advantage.
|