#### Archived

This topic is now archived and is closed to further replies.

# back buffers and primary buffers in directX

This topic is 6508 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey, I have created a tile based game, and I have this idea for how to completely cut down the required processing power, but I dont know how to implement it. Here is my situation. I am making a tile based game (like Civ2), the map size is a 100x100 but only an area of 10x10 actually fits on the monitor, so what I do EVERY game loop is have to calculate which tiles are to be displayed, along with what buildings, structures, terrain enhancements, units, unit color indicators, and unit powerbars along with city names, and the user interface. Now this is RE-calculated every single game loop, which is running over 100fps (one fps is 1 execution of the game loop in my game), but if for example nothing is happening like the game player is just sitting there and not doing anything, all of those calculations are still being performed, every 1/100th of a second. But they dont need to be, but that is not the important stuff. If the user has a unit selected, and he moves that unit around the map, but he doesnt cause the map to scroll, then the only thing that is visually changing is that single unit. So what I want to be able to do is do a calculation for everything that appears on the screen Except that single unit, then blit all of that to the back buffer, then make a copy of that back buffer which will be stored (we will call the copy backbuffer2, the original is backbuffer1). Then calculate where the selected unit is and blit its image to backbuffer1, and then flip backbuffer1 with the primary buffer which then gets displayed on the screen. Then along comes the next game loop (1/100th of a second later) and this time, instead of RE-calculating where everything is, we just calculate where that single selected unit is. After that calculation is done, we copy backbuffer2 onto backbuffer1 and then blit the unit onto backbuffer1 and then flip backbuffer1 with the primary again. What this effectively should do is stop all those uneeded calculations of what appears on the monitor. Of course, if the user where to scroll the map, then we would need to do a recalculation, and make a new backbuffer2 image. So my problem is, how do I make this backbuffer2? I have been able to create 2 back buffers, but the second one gets put into the flipping chain, so for example, you have back1, back2, and the primary, then when you do a flip, the back1 becomes the primary, back2 becomes back1, and the primary becomes back2. How do I prevent back2 from being in the flipping chain? This has stumped me for sometime, so thanks for anyhelp anyone can provide. Possibility

##### Share on other sites
You don''t need a second backbuffer, you need a seperate surface. Just blit all the tiles to the surface, and then blit that to the backbuffer for use in the display. Then when the image is scrolled, you just redraw that surface. As you are going to be using this surface a lot, try to put it in video memory.

##### Share on other sites
So how do I create a surface like you described, where I can blit multiple images to it. When I create the surfaces, they are loaded up when the program starts and each image is its own little surface basically. But I didnt know you could blit to those, I will have to look into that.

Possibility

##### Share on other sites
quote:
Original post by Possibility

So how do I create a surface like you described, where I can blit multiple images to it.
Possibility

Just create a surface (like you would for the small tiles) except size it to be your resolution size. You can blit back and forth to any dx surface any which way you want to. Except the primary... not a good idea.

##### Share on other sites
My question is HOW do you do that. When I load in an image of a tile I use:

terrains_surf[1] = bitmap_surface("terrain1.bmp", (rct);//&the rct gets the deminsions of the image

if (!terrains_surf[1])
{
return FALSE;
}

so in order to create that surface, it uses the bitmap_surface function to load in an image file. And I have tried blitting to one of these surfaces, and that does work, like:

terrains_surf[1]->BltFast(650, 300, terrains_surf[0], &rct, DDBLTFAST_WAIT / DDBLTFAST_SRCCOLORKEY);

it will effectively change terrain type 1 to type 0, and so I guess I could just load up a 1024x768 blank .bmp file and use that as my second back buffer, but then I have the question of would this surface be stored in video memory, and is it as fast as using a real back buffer? But I would love to know how to create a real back buffer for what i want, but I dont think anyone knows how.

Thanks though for letting me know I can BltFast to any surface, even loaded up images.

Possibility

##### Share on other sites
> it will effectively change terrain type 1 to type 0, and
> so I guess I could just load up a 1024x768 blank .bmp file
> and use that as my second back buffer, but then I have the
> question of would this surface be stored in video memory,
> and is it as fast as using a real back buffer? But I would
> love to know how to create a real back buffer for what i
> want, but I dont think anyone knows how.

It is very easy for DirectDraw programmers, but you are using some type of graphics library (bitmap_surface is not part of DirectDraw) that does not let you create surfaces out of nothing. In DirectDraw, you basically just ask the DirectDraw object for a new surface, with the following call:

LPDIRECTDRAWSURFACE lpSurface;

// fill in the ddsd (Direct Draw surface descriptor) with
// all the atributes you want of the new surface, like size
// and so on. In particular, you tell where in memory it
// resides with the following part of the ddsd:

// This will force the surface into regular memory
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY / other_flags;
// This will put the surface into video memory if there is room
ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY / other flags;

// now ask the DirectDraw object (dd) for a new surface:
hrRetVal = lpdd->CreateSurface(&ddsd, &lpSurface, NULL);

I skipped lots of steps that are needed, and if you are using modern versions of DirectX you will need to get a later version of LPDIRECTDRAWSURFACE, but this is the basic idea. Look in your DirectDraw books or other documentation for better samples and more information.

---
Grandpa Simpson - "I never thought I could shoot down a German plane, but last year I proved myself wrong!"

##### Share on other sites
So if the user is doing nothing you have a blazing fast FPS. What happens when the user starts scroller the screen constantly and your AI controlled NPC''s are moving around, etc, etc. ? Your FPS will start dropping and you ill have inconsistant frame-rate. What ever your plan is it should be configured for the lowest common denominator (worst case)

2-cents.

##### Share on other sites
//this assumes lpdd is the correctly set up DDraw7 interface	DDSURFACEDESC2	ddsd;	// working description	LPDIRECTDRAWSURFACE7 lpdds;	// surface	memset(&ddsd,0,sizeof(ddsd));	ddsd.dwSize  = sizeof(ddsd);	ddsd.dwFlags = DDSD_CAPS / DDSD_WIDTH / DDSD_HEIGHT;	ddsd.dwWidth  =  width; //screen width	ddsd.dwHeight =  height; //screen height.	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;//offscreenplain will be in video, if it can fit, then pushed//into system memory.	lpdd->CreateSurface(&ddsd,&lpdds,NULL);

this will create the surface you want. Then just blt this to the primary the part hat you need to. Neither will be erased in the process.

##### Share on other sites
Anonymous Poster: My general idea was to minimize all unnecessary calculations when ever possible. Even when the user is doing nothing, the AI players are still moving and other things are going on, but that stuff is mostlikely happening off screen.

spejic: I do use what you are talking about, here is the way my current setup works:

these are declared globaly
LPDIRECTDRAW7 lpDD=NULL;
LPDIRECTDRAWSURFACE7 lpDDSPrimary=NULL;
LPDIRECTDRAWSURFACE7 lpDDSBack=NULL;
LPDIRECTDRAWSURFACE7 interface_surf=NULL; //the interface
LPDIRECTDRAWSURFACE7 terrains_surf[3]={NULL,NULL,NULL}; //the tile pics

//------ Function to Load a Bitmap into a DirectDraw Surface ------//
LPDIRECTDRAWSURFACE7 bitmap_surface(LPCTSTR file_name,RECT *dims=NULL)
{
HDC hdc;
HBITMAP bit;
LPDIRECTDRAWSURFACE7 surf;

bit = (HBITMAP) LoadImage (NULL, file_name, IMAGE_BITMAP, 0, 0,

if (!bit)
return NULL; //failed to load, return failure to caller

// getting the bitmap dimensions
BITMAP bitmap;
GetObject (bit, sizeof(BITMAP), &bitmap);
int surf_width = bitmap.bmWidth;
int surf_height = bitmap.bmHeight;

// create the surface
HRESULT ddrval;
DDSURFACEDESC2 ddsd;
ZeroMemory (&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_CAPS / DDSD_WIDTH / DDSD_HEIGHT ;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = surf_width;
ddsd.dwHeight = surf_height;

// attempt to create the surface
ddrval = lpDD->CreateSurface (&ddsd, &surf, NULL);

// created ok?
if (ddrval != DD_OK)
{
DeleteObject (bit); // if not created, release the bitmap and return
return NULL; // failure to caller
}
else
{
surf->GetDC (&hdc); //if created, get a DC for the surface

// generate a compatible DC
HDC bit_dc = CreateCompatibleDC (hdc);

// blit the interface to the surface
SelectObject (bit_dc, bit);
BitBlt (hdc, 0, 0, surf_width, surf_height, bit_dc, 0, 0, SRCCOPY);

// releasing the DCs
surf->ReleaseDC (hdc);
DeleteDC (bit_dc);

// save the dimensions if rectangle pointer provided
if (dims)
{
dims->left=0;
dims->top=0;
dims->right=surf_width;
dims->bottom=surf_height;
}
}

// clear the bitmap
DeleteObject (bit);

//return pointer to caller
return surf;
}

{
RECT rct;

interface_surf = bitmap_surface("interface.bmp");
if (!interface_surf)
{
return FALSE;
}

//load the terrain images in the same manner
}

//------ Function to Initialize DirectDraw/DirectInput/DirecSound and the Application ------//
static BOOL Init (HINSTANCE hInstance, int nCmdShow)
{
WNDCLASS wc;
HRESULT ddrval;
LPDIRECTDRAW pDD;

// Create the main DirectDraw object
ddrval = DirectDrawCreate(NULL, &pDD, NULL);
if (ddrval != DD_OK)
{
ErrStr = Err_DirectDrawCreate;
return FALSE;
}

// Fetch DirectDraw4 interface
ddrval = pDD->QueryInterface(IID_IDirectDraw4, (LPVOID *) & lpDD);
if (ddrval != DD_OK) {
ErrStr=Err_Query;
return FALSE;
}

////////////////////////////////////////////////////////////////////////
// Create the primary surface
DDSURFACEDESC2 ddsd;
DDSCAPS2 ddscaps;
ZeroMemory(&ddsd,sizeof(ddsd));
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS / DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE /
DDSCAPS_FLIP /
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );

if (ddrval!=DD_OK)
{
ErrStr=Err_CreatePrimarySurf;
return FALSE;
}

// Create the primary back buffer interface
ddscaps.dwCaps=DDSCAPS_BACKBUFFER;
ddrval=lpDDSPrimary->GetAttachedSurface(&ddscaps,&lpDDSBack);
if (ddrval!= DD_OK)
{
ErrStr=Err_CreateBackSurf;
return FALSE;
}
////////////////////////////////////////////////////////////////////////
}

I have tried in vain in many attempts to figure out that ddsd.ddsCaps.dwCaps which is the last few lines of all that code listed above.

What I think I basically need is to get a complete list of the possible flags one can use when setting up a DD surface. I have looked in the DirectX 7 SDK help thing but that damn thing is cryptic with no comments and such brief explanations that only a 10yr veteran of DirectX programming could only understand it. When i see that stuff its like reading latin to me MS needs to write clearer explanations/comments still.

Possibility

##### Share on other sites
PRIMARYSURFACE means that this surface is the primary surface.
COMPLEX means that it is in a chain of surfaces.
FLIP means that you want the chain of surfaces to be flipable. If you don''t know what that means, pick up a good book.

Then you set 1 backbuffer, so it knows how many surfaces it has in the chain.

Then you create the backbuffer, and add it to the chain by attaching it to the primary surface you just created.
This is trivial stuff, but you have to know it.

The other flags, besides primarysurface complex flip and backbuffer, are offscreen systemmemory and videomemory (or something like that). You use those to create offscreen surfaces for bitmaps and such.

The_Minister

##### Share on other sites
God, this is pissing me off, I have done exactly what iwasbiggs and The Minister suggested, but it just fails, I get my error Fail to Create Surface.

Here was my original code, with out the surface:

LPDIRECTDRAW7 lpDD=NULL;LPDIRECTDRAWSURFACE7 lpDDSPrimary=NULL;LPDIRECTDRAWSURFACE7 lpDDSBack=NULL;// Create the primary surface    DDSURFACEDESC2 ddsd;    DDSCAPS2 ddscaps;    ZeroMemory(&ddsd,sizeof(ddsd));    ddsd.dwSize = sizeof( ddsd );    ddsd.dwFlags = DDSD_CAPS / DDSD_BACKBUFFERCOUNT;    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE /                          DDSCAPS_FLIP /                           DDSCAPS_COMPLEX;    ddsd.dwBackBufferCount = 1;    ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );   	if (ddrval!=DD_OK)	{		ErrStr=Err_CreatePrimarySurf;		return FALSE;	}	// Create the primary back buffer interface	ddscaps.dwCaps=DDSCAPS_BACKBUFFER;	ddrval=lpDDSPrimary->GetAttachedSurface(&ddscaps,&lpDDSBack);	if (ddrval!= DD_OK)	{		ErrStr=Err_CreateBackSurf;		return FALSE;	and here is what I put in to create my new surface:[code]LPDIRECTDRAWSURFACE7 SavedBackSurface=NULL;	//create the second buffer	memset(&ddsd2,0,sizeof(ddsd2));	ddsd2.dwSize  = sizeof(ddsd2);	ddsd2.dwFlags = DDSD_CAPS;	ddsd2.dwWidth  =  x_resolution; //screen width	ddsd2.dwHeight =  y_resolution; //screen height.    ddsd2.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;    ddrval = lpDD->CreateSurface(&ddsd2, &SavedBackSurface, NULL);   	if (ddrval!=DD_OK)	{		ErrStr=Err_CreateStoredBackSurf;		return FALSE;	}

Possibility

Edited by - Possibility on 4/25/00 6:49:24 PM

##### Share on other sites
I''m pretty sure I understand what you''re trying to do, Possibility. The way I see it when it comes to refreshing the whole screen for every frame is like this. Since the whole screen will be refreshing at one point in time, it won''t hurt to refresh the screen every frame. If it can''t handle being refreshed for every frame, optimization needs to be done somewhere.

##### Share on other sites
Looks like there are a few things wrong with the code (that is if you did a direct copy and paste).

For example, when creating the new surface (everything else seemed alright at a first glance), you forgot a few flags.

quote:
Original post by Possibility

God, this is pissing me off, I have done exactly what iwasbiggs and The Minister suggested, but it just fails, I get my error Fail to Create Surface.

Here was my original code, with out the surface:

and here is what I put in to create my new surface:

LPDIRECTDRAWSURFACE7 SavedBackSurface=NULL;	//create the second buffer	memset(&ddsd2,0,sizeof(ddsd2));	ddsd2.dwSize  = sizeof(ddsd2);	ddsd2.dwFlags = DDSD_CAPS;	ddsd2.dwWidth  =  x_resolution; //screen width	ddsd2.dwHeight =  y_resolution; //screen height.    ddsd2.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;    ddrval = lpDD->CreateSurface(&ddsd2, &SavedBackSurface, NULL);   	if (ddrval!=DD_OK)	{		ErrStr=Err_CreateStoredBackSurf;		return FALSE;	}

You forgot the DDSD_WIDTH and DDSD_HEIGHT flags when creating the surface. I think this is all you need to fix, hope this fixes it. (My above code, copied exactly, should work.)

You also might want to change your error checking code to the "FAILED()" macro because in future versions of dx (at least MS sugguests) there might return values that have succedded, but they may not be equal to DD_OK. So in the if tests it''s a good idea to use:
if(FAILED(h_result)){
//error stuff
}

Reply again if stuff is still not working. I''ll get back to this board in about 4 hours

##### Share on other sites
Make a new surface and call it something like "terrain". Whenever the screen scolls, redraw Terrain. However, every update loop, you just draw the "Terran" surface to the back buffer. You now have 1 blit instead of 100 every update.
-Michael

##### Share on other sites
I actually had the DDSD_HEIGHT AND WIDTH in there, but it didnt work. I tried it about 10 different ways with different flags and other stuff. and I tried it about 50 different ways before comming to this board, and every single time it fails.

The only way I can do it is by using my:
interface_surf = bitmap_surface("interface.bmp");

which loads an bmp image. So I just made a blank 800x600 image and loaded it up, and then I use that as my temporary back buffer.

Possibility

##### Share on other sites
Just wait until DirectX comes out with a function to do that!!!

-- Goodlife

-----------------------------
Think of your mind as a door on a house. Leave the door always closed, and it's not a house, it's a prison. Leave the door always open, and it's not a house, it's a wilderness-- all the vermin creep in.