Jump to content
  • Advertisement

Archived

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

Rooter

Speeding up "2d" rendering in Direct3D 8?

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, My engine is not NEARLY as fast as I wish it is. We are currently rendering as follows: there are 20 * 15 tiles on screen at once in a layer, this is 300 tiles. There are 4 layers. That''s 1200 tiles. For each tile we have one quad set up and we just move it around the screen, like so:
void CMap::RenderMap(int layer){
	int a;
	float xval = dimx - 1.0;
	float yval = dimx - 1.0;
	a = -1;
	dx->d3dDevice->SetVertexShader ( MAP_VERTEX_FVF );

	for ( float y = abs(camy) ; y < (16.0 + abs(camy)) ; y++ )
	{
		yval = y;
		for ( float x = abs(camx) ; x < (21.0 + abs(camx)) ; x ++ )
		{
			xval = x;
			D3DXMatrixTranslation ( &matWorld , ( float ) xval + camx, ( float ) yval + camy , 0.0 ) ;
			
			// transform
			dx->d3dDevice->SetTransform ( D3DTS_WORLD, &matWorld ) ;
			
			//set the material
			dx->d3dDevice->SetMaterial ( &MapMaterial) ;

			if(y > dimy - 2.0)
				yval = dimy - 1.0;

			if(x > dimx - 2.0)
				xval = dimx - 1.0;

			if (a != mapdata[layer][int(xval)][int(yval)])
			{
				dx->d3dDevice->SetTexture(0, tileset->GetTile(mapdata[layer][int(xval)][int(yval)]));
				a = mapdata[layer][int(xval)][int(yval)];
			}
			if ((layer == 2) || (layer == 3))
			{
								// Set the source blend state.
				dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
 
				// Set the destination blend state.
				dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

				dx->d3dDevice->DrawPrimitiveUP ( D3DPT_TRIANGLESTRIP , 2 , map , sizeof ( MapVertex ) ) ;
			}
			else
			{

								// Set the source blend state.
				dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
 
				// Set the destination blend state.
				dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);

				dx->d3dDevice->DrawPrimitiveUP ( D3DPT_TRIANGLESTRIP , 2 , map , sizeof ( MapVertex ) ) ;
			}
		}
	}
} 
Now, this isn''t very fast. I figure there HAS to be a way to quickly render all of this as 3d games obviously render perhaps 1000 of times of this amount and doesn''t slow down at all. Any thoughts, suggestions? Thanks, Robert Preston

Share this post


Link to post
Share on other sites
Advertisement
How many FPS do you get with that? I guess the slowdown comes form the UP functions. Have you thought of using a dynamic VB?

Share this post


Link to post
Share on other sites
Well, its not so much that it runs slow... It runs around 55 FPS on a 650 MHZ Athlon, Geforce 2 MX 200 w/ all 4 layers on, sprite layer and 10,000 snow or rain particles and lighting. But it has to run faster, my friend can run Counter-strike on his 400 P2 w/ TNT2 and our game runs at like 10 FPS on there... so there must be another way... what is dynamic VB?

[edited by - Rooter on April 20, 2003 1:25:57 PM]

Share this post


Link to post
Share on other sites
Some easy things:
1)You set the material to a global. If each tile uses the same material you only need to set it once. Either do it at the top with SetVertexShader or do it before you loop through the layers and call Render.
2)Your blend states are based on the layer. The layer is a parameter, so move the code to set the blend states up to the top of the function. Since they don''t change within a layer, set them once.
3)All of your numeric values seem to be ints, so why use floats and cast them to ints every single time they are used? Change xval/yval to be ints.
4)Shouldn''t float yval = dimx - 1.0; be float yval = dimy - 1.0;?

All of those changes can be made quite easily. The biggest gain will be from batching though. Drawing your tiles 2 triangles at a time is quite slow. The more you draw at a time (within reason) the better.

For example, you could draw all tiles of type 0 at a time with a single Draw call.

Also, DrawPrimitiveUP is slow. Switching to use Vertex Buffers would be better.


Stay Casual,

Ken
Drunken Hyena

Share this post


Link to post
Share on other sites
How do I do these vertex buffers... Here''s what I just tried taking your suggestions:

in the initialize function:
dx->d3dDevice->CreateVertexBuffer ( 1 * sizeof ( MapVertex ) , 0 , MAP_VERTEX_FVF , D3DPOOL_DEFAULT , &vBuf ) ;
dx->d3dDevice->SetStreamSource ( 0 , vBuf , sizeof ( MapVertex ) ) ;
BYTE *buffer;
vBuf->Lock ( 0 , 0 , &buffer , 0 ) ;

memcpy ( buffer , map, 1 * sizeof ( MapVertex ) ) ;

vBuf->Unlock ( ) ;

the new render function:
int a;
float xval = dimx - 1.0;
float yval = dimy - 1.0;
a = -1;
dx->d3dDevice->SetVertexShader ( MAP_VERTEX_FVF );
dx->d3dDevice->SetMaterial ( &MapMaterial) ;
dx->d3dDevice->SetStreamSource ( 0 , vBuf , sizeof ( MapVertex ) ) ;

if ((layer == 2) || (layer == 3))
{
dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
}
else
{
dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}

for ( float y = abs(camy) ; y < (16.0 + abs(camy)) ; y++ )
{
yval = y;
for ( float x = abs(camx) ; x < (21.0 + abs(camx)) ; x ++ )
{
xval = x;
D3DXMatrixTranslation ( &matWorld , ( float ) xval + camx, ( float ) yval + camy , 0.0 ) ;

dx->d3dDevice->SetTransform ( D3DTS_WORLD, &matWorld ) ;

if(y > dimy - 2.0)
yval = dimy - 1.0;

if(x > dimx - 2.0)
xval = dimx - 1.0;

if (a != mapdata[layer][int(xval)][int(yval)])
{
dx->d3dDevice->SetTexture(0, tileset->GetTile(mapdata[layer][int(xval)][int(yval)]));
a = mapdata[layer][int(xval)][int(yval)];
}

// dx->d3dDevice->DrawPrimitiveUP ( D3DPT_TRIANGLESTRIP , 2 , map , sizeof ( MapVertex ) ) ;
dx->d3dDevice->DrawPrimitive ( D3DPT_TRIANGLESTRIP , 0 , 2 ) ;
}
}

Share this post


Link to post
Share on other sites
G''day!

That looks better. To really get good usage out of Vertex buffers you need to draw more tiles at one time.Here are some quick ideas:

Say we have 2 different tiles (grass and dirt). You have 20x15 tile visibility, so 300 tiles => 600 triangles.

Create your vertex buffer to hold 600 triangles. This is the maximum number of any given tile per screen. Your vertex buffer should be created as DYNAMIC and WRITE_ONLY. Your vertex format should use XYZRHW rather than XYZ. You''ll be using triangle lists rather than strips.

In Render (bad pseudo-code):
foreach tile_type:
Lock the VB with DISCARD
Loop through the visible map, if the tile matches the currenttile type, add it to the VB
Unlock the VB
SetTexture the current tile texture
DrawPrimitive however many triangles are in the buffer


I think that made sense. There are a LOT of different methods (using Indexed lists, batching textures together, using Ortho projection rather than XYZRHW, etc, etc) but if you get this much working you should see a significant increase in framerate.

Stay Casual,

Ken
Drunken Hyena

Share this post


Link to post
Share on other sites
Thanks a lot Ken! that''s more what I was thinking... the way im doing it now doesnt render ANYTHING :D I''ll read through your last post and try to make some sense of it tommorow and put my head together with our other programmer since thats actually his function... he''s just draggin his feet on optimizing it so I figured I''d jump all over it. Thanks a lot for the good input, if I can''t figure it out i''ll repost in here again!

-Rob Preston

Share this post


Link to post
Share on other sites
Welp, I''ve been working on this an dhere''s a new set of problems... the way my code is now it has a bunch of weird lines on the screen and 1 or 2 tiles. I think im not setting up the buffer the right way. I guess you cant use memcpy when you are copying more than one quad at at ime or something? Here''s hte code...


Initialize:
dx->d3dDevice->CreateVertexBuffer ( 6 * sizeof ( MapVertex ) , D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC , MAP_VERTEX_FVF , D3DPOOL_DEFAULT , &vBuf ) ;
dx->d3dDevice->SetStreamSource ( 0 , vBuf , sizeof ( MapVertex ) ) ;

Render function:
int a,b,c, numQuads = 0, d, cx, cy;
float xval = dimx - 1.0;
float yval = dimy - 1.0;
BYTE* buffer ;

a = -1;

for (a=0;a<21;a++)
{
for (b=0;b<16;b++)
{
map[(b*21)+a][0].pos.x = abs(camx) + a;
map[(b*21)+a][0].pos.y = abs(camy) + b;
map[(b*21)+a][1].pos.x = abs(camx) + a + 1.0;
map[(b*21)+a][1].pos.y = abs(camy) + b;
map[(b*21)+a][2].pos.x = abs(camx) + a;
map[(b*21)+a][2].pos.y = abs(camy) + b + 1.0;
map[(b*21)+a][3].pos.x = abs(camx) + a + 1.0;
map[(b*21)+a][3].pos.y = abs(camy) + b + 1.0;
}
}

dx->d3dDevice->SetVertexShader ( MAP_VERTEX_FVF );
dx->d3dDevice->SetStreamSource ( 0 , vBuf , sizeof ( MapVertex ) ) ;
dx->d3dDevice->SetMaterial ( &MapMaterial) ;

if ((layer == 2) || (layer == 3)){
dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
}
else{
dx->d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
dx->d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}

D3DXMatrixTranslation ( &matWorld , ( float ) camx, ( float ) camy , 0.0 ) ;
dx->d3dDevice->SetTransform ( D3DTS_WORLD, &matWorld ) ;

for (d=0;dNoTiles();d++)
{
vBuf->Lock(0,0, &buffer, D3DLOCK_DISCARD);
for (a=0;a<21;a++)
{
for (b=0;b<16;b++)
{
if (mapdata[layer][icamx + a][icamy + b] == d)
{
memcpy(buffer, map[(b*21) + a], 6 * sizeof(MapVertex));
numQuads++;
}
}
}
vBuf->Unlock();
dx->d3dDevice->SetTexture(0, tileset->GetTile(mapdata[layer][icamx + a][icamy + b])); a = mapdata[layer][int(xval)][int(yval)];
dx->d3dDevice->DrawPrimitive ( D3DPT_TRIANGLESTRIP , 0 , 2 * numQuads) ;
numQuads = 0;
}

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!