Many materials = huge FPS hit

Started by
3 comments, last by BS-er 21 years, 6 months ago
My modeller friend sent me an X file for a unit in my game. This one mesh caused a tremendous performance hit, more so than rendering 50 other X file meshes with even larger numbers of faces than the one new mesh. the big difference that I observed is that the new unit has 20 different materials. I don't see how this should have such a nasty hit though. I use a standard approach (I think) for loading and rendering the X-file. Below are several functions of the class, in case anyone can spot something wrong. Any help would be most appreciated.
    
// ______________________________________________________________________________________

HRESULT CGraphicsMesh::LoadXFile( char* pstrPathName )
{
	HRESULT r = 0;

	// The buffer to hold the materials

	LPD3DXBUFFER pMaterialBuffer = 0;

	// Load the x file from disk

	r = D3DXLoadMeshFromX( pstrPathName, D3DXMESH_MANAGED /*D3DXMESH_SYSTEMMEM*/, g_pDevice, 0, 
							&pMaterialBuffer, (DWORD*)&m_NumMats, &m_pMesh );
	if( FAILED( r ) )
	{
		SetError( "Failed to load XFile with file name:" );
		SetError( pstrPathName );
		return E_FAIL;
	}

	// Create a new texture array

	m_pTextures = new LPDIRECT3DTEXTURE8[ m_NumMats ];
	// Create a new material array

	m_pMaterials = new CGraphicsMaterial[ m_NumMats ];

	// Get a pointer to the start of the material buffer

	D3DXMATERIAL* pMaterials = (D3DXMATERIAL*)pMaterialBuffer->GetBufferPointer();

	// Loop for each material in the buffer

	for( int i = 0 ; i < m_NumMats ; i++ )
	{


		// Extract the material from the buffer

		m_pMaterials[i].m_Material = pMaterials[i].MatD3D;
		// Brighten the material

		m_pMaterials[i].m_Material.Ambient = m_pMaterials[i].m_Material.Diffuse;
		
		// If a texture is not set for this material

		if( !pMaterials[i].pTextureFilename )
		{
			// Set the texture to the default texture 

			m_pTextures[i] = g_pDefaultTexture;

			// Iterate to the next loop because there is no texture

			continue;
		}
		
		// Create a new texture from the filename supplied

		r = D3DXCreateTextureFromFile( g_pDevice, pMaterials[i].pTextureFilename, &m_pTextures[i] );
		if( FAILED( r ) )
		{
			SetError( "Unable to load texture for mesh with filname:" );
			SetError( pMaterials[i].pTextureFilename );
			
			// If the texture load failed then set

			// it to the default texture

			m_pTextures[i] = g_pDefaultTexture;
		}
	}

	// Release the material buffer

	pMaterialBuffer->Release();

	return S_OK;

}


// ______________________________________________________________________________________

HRESULT CGraphicsMesh::Render()
{
	HRESULT r = E_FAIL;

	g_pDevice->SetVertexShader( GRAPHICS_VERTEX_TYPE );

	// Loop for each material

	for( int i = 0 ; i < m_NumMats; i=i+1 )
	{
		// Set this material as active

		m_pMaterials[i].Update();
		// Set this texture as active

		g_pDevice->SetTexture( 0, m_pTextures[i] );
		// Render this subset of the mesh

		r = m_pMesh->DrawSubset(i);

		// Reset the vertex shader	

	}

	// Return the result of the render operation

	return r;
}
     
The one foriegn function in there is the following:
  
// Set this material as active

// ______________________________________________________________________________________

HRESULT CGraphicsMaterial::Update()
{
	return g_pDevice->SetMaterial( &m_Material );
}
  
Value of good ideas: 10 cents per dozen. Implementation of the good ideas: Priceless. Proxima Rebellion - A 3D action sim with a hint of strategy [edited by - BS-er on October 3, 2002 7:07:34 AM]
Value of good ideas: 10 cents per dozen.Implementation of the good ideas: Priceless.Machines, Anarchy and Destruction - A 3D action sim with a hint of strategy
Advertisement
1) Maybe 20 texture maps is too much for the available video memory of your graphics card. Overcommit too much and things go slower because some textures have to be uploaded halfway through the frame (i.e. a lots of AGP bus data transfer). For 20 say 512x512x32 textures that''s 20Mb of video memory - that''s before you include things like the framebuffer (which may be up to say 16x the size of the visible buffer for supersampled antialiasing!) etc etc

2) Changing texture causes the graphics card to flush all polygons rendered with the previous texture. This means that the whole pipeline stalls while the card finishes rendering with that texture and no new graphics can be submitted. Graphics hardware likes to buffer up lots of data and draw it delayed.

3) If there are any places where the size of a texture on screen when applied to a 3d polygon is much smaller than the actual texture itself (i.e. the texture has been shrunk), then this can be quite a hit in performance terms. You should definately use mipmapping in situations like this. Or reduce the texture sizes significantly.

--
Simon O''Connor
Creative Asylum Ltd
www.creative-asylum.com

Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site

I ran into this same problem until I started using a texture manager to make sure I wasn''t creating multiple texture objects for the same physical texture.

Does the model only have one texture file? If so, you''re duplicating it 20 different times, once for each material.
Just a thought...

If you are drawing the mesh lots of times, Set the texture and Mat then draw the subset for all the meshes...next texture and mat, draw Next subset etc...

Looks like each time you draw now, you run through all the textures and Mats for each mesh. I think it would be faster to set the world matrix then to change Textures that much.
Thanks for your suggestions. Alas I''m still unable to find the culprit. Here are my findings:

1) My loading code and rendering code for X file meshes appears virtually identical to example code that comes with the DX8.1 SDK, except for minor, clearly dismissable differences.

2) When I strip my code so that only the mesh is rendered, I get 90 FPS while the DX8.1 example code gets around 1800 FPS.

3) When I render nothing but a blank screen, my FPS shoots up past 7000, telling me that its the mesh render that is clogging the pipeline.

4) The mesh has 20 materials, one 256x256 texture and 265 vertices, which is reasonable. The DX code handles it just fine. it is interesting that the DX8.1 code runs at 20x the FPS of my code though (correlation??? Hmmm).

5) tweaking the code so that all 20 materials point to one texture had no improvement.

6) I''ve checked for the usual blunders. The call to render the mesh is made only once. It calls ID3DXMesh::DrawSubset() 20 times, one for each material, just like the DX8.1 example code.

Given that everything "looks" as it should, I wonder if there is some sort of setup issue that can cause slowness, especially with a mesh that has many materials.

I would gladly be in debt to anyone who can help me.

Value of good ideas: 10 cents per dozen.
Implementation of the good ideas: Priceless.
Proxima Rebellion - A 3D action sim with a hint of strategy
Value of good ideas: 10 cents per dozen.Implementation of the good ideas: Priceless.Machines, Anarchy and Destruction - A 3D action sim with a hint of strategy

This topic is closed to new replies.

Advertisement