Flipping Surfaces and GDI's Frame Rate

Any surface in DirectDraw can be constructed as a flipping surface. A flipping surface is simply any piece of memory that can be swapped between a front buffer and a back buffer. Constructing a DirectDraw surface as a flipping surface has many advantages over the traditional, limited scope of page flipping.

When an application uses the IDirectDrawSurface2::Flip method to request a flip operation, the surface memory areas associated with the DirectDrawSurface objects being flipped are switched. Surfaces attached to the DirectDrawSurface objects being flipped are not affected. For example, in a double-buffered situation, an application that draws on the back buffer always uses the same DirectDrawSurface object. The surface memory underneath the object is simply switched with the front buffer when IDirectDrawSurface2::Flip is requested.

If the front buffer is visible, either because it is the primary surface or because it is an overlay that is currently visible, subsequent calls to the IDirectDrawSurface2::Lock or IDirectDrawSurface2::Blt methods that target the back buffer fail with the DDERR_WASSTILLDRAWING return value until the next vertical refresh occurs. This occurs because the front buffer's previous surface memory, which is no longer attached to the back buffer, is still being drawn to the physical display by the hardware. This situation disappears during the next vertical refresh because the hardware that updates the physical display re-reads the location of the display memory on every refresh.

This physical requirement makes calling the IDirectDrawSurface2::Flip method on visible surfaces an asynchronous command. When building games, you should, for example, perform all of the non-visual elements of the game after this method is called. When the input, audio, game-play, and system-memory drawing operations have been completed, you can begin the drawing tasks that require gaining access to the visible back buffers.

When your application needs to run in a window and still requires a flipping environment, it will attempt to create a flipping overlay surface. If the hardware does not support overlays, you can create a primary surface that page flips. When a surface is about to become the primary surface and GDI does not have information about that surface, you can blit the contents of the primary surface that GDI is writing to onto the buffer that is about to become visible. This takes little, if any, processing time because the blits are performed asynchronously. It can, however, consume considerable blitter bandwidth that is dependent on screen resolution and the size of the window that is being page flipped. As long as the frame rate does not dip below 20 frames per second, GDI will appear to be operating correctly.

Before you instantiate a DirectDraw object, GDI is already using your display memory to display itself. When you call DirectDraw to instantiate a primary surface, the memory address of that surface will be the same as GDI is currently using.

If you create a complex surface with a back buffer, GDI will first point to the display memory for the primary surface. Because GDI was created before DirectDraw, GDI cannot be informed of DirectDraw's actions. Therefore, GDI will continue operating on this surface, even if you have flipped it and it is now the non-visible back buffer.

Many applications begin by creating one large window that covers the entire screen. As long as your application is active and has the focus, GDI will not attempt to write into its copy of the buffer because nothing it controls needs redrawing.

For other scenarios, remember that GDI has information about the original surface only, and it has no information about whether it is currently the primary surface or a back buffer. If you do not need the GDI screen, you can use the technique described above. If you do need GDI, you can try the following technique:

1Create a primary surface with two back buffers.

2Blit the initial primary surface (the GDI surface) to the middle back buffer.

3Flip the surfaces (with the lpDDSurfaceTargetOverride parameter set to NULL) to put GDI into last place and make your initial copy visible.

After you have done this, you can copy from the GDI buffer to the middle buffer, draw what you want the user to see on that buffer, you can then keep GDI safely on the bottom and oscillate between the other two buffers, by using the following example:

pPrimary->Flip(pMiddle);