Throughout the book I've mentioned that you can create a number of different types of surfaces, but up to this point you've only seen how to work with primary surfaces. Now I want to talk about offscreen surfaces. Basically, there are two types of offscreen surfaces. The first kind is the back buffer.
Back buffers are surfaces used in an animation chain that have the same geometry and color depth as the primary surface. Back buffer surfaces are unique because you create them as you create the primary surface. They're part of the primary surface's flipping chain. In other words, when you request one or more secondary surfaces to be back buffers, by default DirectDraw assumes that you'll be using them in an animation loop. Figure 7.10 shows the relationship between the primary surface and secondary surfaces that are back buffers.
The reason you would create a back buffer is to emulate the functionality of double buffering, but in a more DirectDraw kind of way. If you create a DirectDraw back buffer, usually it will be in VRAM and thus will be very fast. Moreover, you'll be able to page flip it with the primary surface, which is much faster than the memory copy needed for a double buffering scheme.
Technically, you can have as many back buffers as you want in a flipping chain. However, at some point you'll run out of VRAM and the surface will have to be created in system memory, which is much slower. In general, if you create an (m x n) mode with a color depth of one byte, the amount of memory for the primary buffer is of course m*n bytes (unless there's memory pitch alignment). Therefore, if you have one extra back buffer secondary surface, you would multiply this by 2 because back buffers have the same geometry and color depth. So 2*m*n bytes would be the memory required. Finally, if the color depth is 16-bit, you would have to scale all the calculations by two bytes, and similarly for 32-bit buffers you would scale by 4. For example, the primary buffer for a 640x480x16-bit mode would take
Width * Height * Number of bytes per pixel 640 * 480 * 2 = 614,400 bytes
And if you want one extra back buffer, you need to multiply that result by 2 so the final number of bytes is
614,400 * 2 = 1,228,800 bytes
Roughly 1.2MB of VRAM! Hence, if you have only a 1MB card, you can forget having a VRAM back buffer in 640x480x16-bit color mode. Most cards have at least 2MB these days, so you're usually safe, but it's always good to test for the amount of memory available on the card. You can do so with a GetCaps class function. We'll cover that at the end of the chapter. Now, most video cards these days have 8–64 megs of VRAM, but lots of value computers have shared memory and you can easily find yourself out of luck with a 2–4 meg card on a value machine.
HRESULT GetAttachedSurface( LPDDSCAPS2 lpDDSCaps, LPDIRECTDRAWSURFACE7 FAR *lplpDDAttachedSurface );
DDSCAPS2 ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
Or just use the DDSCAPS2 field of the DDSURFACEDESC2 structure to save another variable, like this:
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
// assume we already have the directdraw object etc... DDSURFACEDESC2 ddsd; // directdraw surface description LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // primary surface LPDIRECTDRAWSURFACE7 lpddsback = NULL; // back buffer // clear ddsd and set size DDRAW_INIT_STRUCT(ddsd); // enable valid fields ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // set the backbuffer count field to 1 ddsd.dwBackBufferCount = 1; // request a complex, flippable ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; // create the primary surface if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL))) return(0); // now query for attached surface from the primary surface // this line is needed by the call ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER; if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback))) return(0);
At this point, lpddsprimary points to the primary surface, which is currently visible, and lpddsback points to the back buffer surface, which is not. Take a look at Figure 7.11 to see this graphically. To access the back buffer, you can lock/unlock it just like the primary surface.
// copy the double buffer into the primary buffer DDRAW_INIT_STRUCT(ddsd); // lock the back buffer surface lpddsback->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // now ddsd.lpSurface and ddsd.lPitch are valid // do whatever... // unlock the back buffer, so hardware can work with it lpddsback->Unlock(NULL);
Now, the only problem is that you don't know how to flip the pages, or, in other words, make the back buffer surface the primary surface and hence animate the two pages. Let me show you how that's done!