Sign in to follow this  
John_Idol

ID3DXAllocateHierarchy - Memory Leaks

Recommended Posts

John_Idol    126
Hi all, I've been fighting with some memory leaks while loading the mesh. I was able to set the BreakAllocID (it works on my machine at home but doing exactly the same things it doesn't here at work...) on the control panel and looking at the call stack I isolated my last call: HRESULT hr = D3DXLoadMeshHierarchyFromX(filename, D3DXMESH_MANAGED, device, &Alloc, NULL, &m_frameRoot, &m_animController); Does this inequivocally mean that there's something not being freed in the implementation of ID3DXAllocateHierarchy I am using(http://www.toymaker.info/Games/html/load_x_hierarchy.html)? I got the same meory leaks with the demo itself so it's not something I added (I didn't add a thing in the ID3DXAllocateHierarchy implementation). I keep going through the code but I can't find nothing not being freed. My guess would be it is some field populated by the DirectX runtime which I am supposed to free. Shall I post the class cpp? Thanks in advance, JI

Share this post


Link to post
Share on other sites
John_Idol    126
Ok, Here we go: this is the implementation I am using, everything looks alright to me.
I am posting just the Create/Destroy frame methods 'cause they're smaller so if anyone is able to find something wrong here i can avoid to post the mesh container stuff (something tells me I am gonna have to...).


struct D3DXFRAME_EXTENDED: public D3DXFRAME
{
D3DXMATRIX exCombinedTransformationMatrix;
};

HRESULT CMeshHierarchy::CreateFrame(LPCSTR Name, LPD3DXFRAME *ppNewFrame)
{
// Always a good idea to initialise return pointer before proceeding
*ppNewFrame = NULL;

// Create a new frame using the derived version of the structure
D3DXFRAME_EXTENDED *newFrame = new D3DXFRAME_EXTENDED;
ZeroMemory(newFrame,sizeof(D3DXFRAME_EXTENDED));

// Now fill in the data members in the frame structure

// The frame name (note: may be null or zero length)
newFrame->Name=CUtilities::DuplicateCharString(Name);

// Now initialize other data members of the frame to defaults
D3DXMatrixIdentity(&newFrame->TransformationMatrix);
D3DXMatrixIdentity(&newFrame->exCombinedTransformationMatrix);

newFrame->pMeshContainer = NULL;
newFrame->pFrameSibling = NULL;
newFrame->pFrameFirstChild = NULL;

// Assign the return pointer to our newly created frame
*ppNewFrame = newFrame;

return S_OK;
}

HRESULT CMeshHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree)
{
// Convert to our extended type. OK as we know for sure it is:
D3DXFRAME_EXTENDED *pFrame = (D3DXFRAME_EXTENDED*)pFrameToFree;

SAFE_DELETE_ARRAY( pFrame->Name );
SAFE_DELETE( pFrame );

return S_OK;
}



Thanks!

JI

Share this post


Link to post
Share on other sites
carllloyd    124
I'm not 100% sure so this is more a comment but have you checked that the number of frames that you free matches the number that are actually created in the hierarchy?

Share this post


Link to post
Share on other sites
John_Idol    126
Fact is I am not calling manually Create/DestryFrame, they're callback methods and I'm just implementing them.
When you load the x file with Hierarchy with D3DXLoadMeshHierarchyFromX everything is managed automatically, so as long as I am deallocating in DestroyFrame everything which is been allocated in CreateFrame it should be fine.

I was hoping someone more experienced than me could spot something I do not see.


Share this post


Link to post
Share on other sites
John_Idol    126
I implement it but I don't call Create/DestroyMeshContainer, 'cause as for Create/DestroyFrame, they are callbacks. I checked and the code is being executed.

these are the functions I am using (from a demo which web address you can find on top-post):


struct D3DXMESHCONTAINER_EXTENDED: public D3DXMESHCONTAINER
{
/* The base D3DXMESHCONTAINER has a pMaterials member which is a D3DXMATERIAL structure that contains a texture filename and material data. It is easier to ignore this and instead store the data in arrays of textures and materials in this extended structure:*/
IDirect3DTexture9** exTextures; // Array of texture pointers
D3DMATERIAL9* exMaterials; // Array of materials

// Skinned mesh variables
ID3DXMesh* exSkinMesh; // The skin mesh
D3DXMATRIX* exBoneOffsets; // The bone matrix Offsets, one per bone
D3DXMATRIX** exFrameCombinedMatrixPointer; // Array of frame matrix pointers
};

HRESULT CMeshHierarchy::CreateMeshContainer( THIS_
LPCSTR Name,
CONST D3DXMESHDATA *pMeshData,
CONST D3DXMATERIAL *pMaterials,
CONST D3DXEFFECTINSTANCE *pEffectInstances,
DWORD NumMaterials,
CONST DWORD *pAdjacency,
LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER* ppNewMeshContainer)
{
// Create a mesh container structure to fill and initilaise to zero values
// I use an extended version of the structure (D3DXMESHCONTAINER_EXTENDED)
D3DXMESHCONTAINER_EXTENDED *newMeshContainer=new D3DXMESHCONTAINER_EXTENDED;
ZeroMemory(newMeshContainer, sizeof(D3DXMESHCONTAINER_EXTENDED));

// initialise return pointer before proceeding
*ppNewMeshContainer = NULL;

// The mesh name (may be null) needs copying over
newMeshContainer->Name=CUtilities::DuplicateCharString(Name);

// The mesh type (D3DXMESHTYPE_MESH, D3DXMESHTYPE_PMESH or D3DXMESHTYPE_PATCHMESH)
if (pMeshData->Type!=D3DXMESHTYPE_MESH)
{
// This does not handle mesh types other than the standard
DestroyMeshContainer(newMeshContainer);
return E_FAIL;
}

newMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

// Adjacency data - holds information about adjacency, required by ID3DMESH
DWORD dwFaces = pMeshData->pMesh->GetNumFaces();
newMeshContainer->pAdjacency = new DWORD[dwFaces*3];
memcpy(newMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * dwFaces*3);

// Get the Direct3D device, luckily this is held in the mesh itself
LPDIRECT3DDEVICE9 pd3dDevice = NULL;
pMeshData->pMesh->GetDevice(&pd3dDevice);

// Make a copy of the mesh data that is passed in to our mesh container
D3DVERTEXELEMENT9 Declaration[MAX_FVF_DECL_SIZE];
if (FAILED(pMeshData->pMesh->GetDeclaration(Declaration)))
return E_FAIL;

pMeshData->pMesh->CloneMesh(D3DXMESH_MANAGED,
Declaration, pd3dDevice,
&newMeshContainer->MeshData.pMesh);


// Create material and texture arrays. I always want to have at least one
newMeshContainer->NumMaterials= max(NumMaterials,1);
newMeshContainer->exMaterials= new D3DMATERIAL9[newMeshContainer->NumMaterials];
newMeshContainer->exTextures= new LPDIRECT3DTEXTURE9[newMeshContainer->NumMaterials];

ZeroMemory(newMeshContainer->exTextures,sizeof(LPDIRECT3DTEXTURE9)* newMeshContainer->NumMaterials);

if (NumMaterials>0)
{
// Load all the textures and copy the materials over
for(DWORD i = 0; i < NumMaterials; ++i)
{
newMeshContainer->exTextures[i] = NULL;
newMeshContainer->exMaterials[i]=pMaterials[i].MatD3D;

if(pMaterials[i].pTextureFilename)
{
// Note: the texture filename in the mesh container is an LPSTR (char *) but this
// demo uses UNICODE so convert to LPWSTR (WCHAR*) required by D3DXCreateTextureFromFileW
WCHAR wszTexName[MAX_PATH];
CUtilities::FillWideStringFromCharString(pMaterials[i].pTextureFilename,wszTexName,MAX_PATH);

// Use the D3DX function to create the texturer
if(FAILED(D3DXCreateTextureFromFile(pd3dDevice, wszTexName,&newMeshContainer->exTextures[i])))
{
newMeshContainer->exTextures[i] = NULL;
}
}
}
}
else
// make a default material in the case where the mesh did not come with one
{
ZeroMemory(&newMeshContainer->exMaterials[0], sizeof( D3DMATERIAL9 ) );
newMeshContainer->exMaterials[0].Diffuse.r = 0.5f;
newMeshContainer->exMaterials[0].Diffuse.g = 0.5f;
newMeshContainer->exMaterials[0].Diffuse.b = 0.5f;
newMeshContainer->exMaterials[0].Specular = newMeshContainer->exMaterials[0].Diffuse;
newMeshContainer->exTextures[0]=NULL;
}

// If there is skin data associated with the mesh copy it over
if (pSkinInfo)
{
// save off the SkinInfo
newMeshContainer->pSkinInfo = pSkinInfo;
pSkinInfo->AddRef();

// Need an array of offset matrices to move the vertices from the figure space to the bone's space
UINT numBones = pSkinInfo->GetNumBones();
newMeshContainer->exBoneOffsets = new D3DXMATRIX[numBones];

// Create the arrays for the bones and the frame matrices
newMeshContainer->exFrameCombinedMatrixPointer = new D3DXMATRIX*[numBones];

// get each of the bone offset matrices so that we don't need to get them later
for (UINT i = 0; i < numBones; i++)
newMeshContainer->exBoneOffsets[i] = *(newMeshContainer->pSkinInfo->GetBoneOffsetMatrix(i));

/* Note: in the Microsoft samples a GenerateSkinnedMesh function is called here in order to prepare the skinned mesh data for optimial hardware acceleration. As mentioned in the notes this sample does not do hardware skinning but instead uses software skinning*/
}
else
{
// No skin info so null all the pointers
newMeshContainer->pSkinInfo = NULL;
newMeshContainer->exBoneOffsets = NULL;
newMeshContainer->exSkinMesh = NULL;
newMeshContainer->exFrameCombinedMatrixPointer = NULL;
}

// When we got the device we caused an internal reference count to be incremented
// So we now need to release it
SAFE_RELEASE(pd3dDevice);

// The mesh may contain a reference to an effect file
if (pEffectInstances)
{
if (pEffectInstances->pEffectFilename)
{
OutputDebugString(L"This .x file references an effect file. Effect files are not handled by this demo");
}
}

// Set the output mesh container to our newly created one
*ppNewMeshContainer = newMeshContainer;

return S_OK;
}

HRESULT CMeshHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase)
{
// Convert to our extended type. OK as we know for sure it is:
D3DXMESHCONTAINER_EXTENDED* pMeshContainer=(D3DXMESHCONTAINER_EXTENDED*)pMeshContainerBase;

// name
SAFE_DELETE_ARRAY(pMeshContainer->Name)

// material array
SAFE_DELETE_ARRAY(pMeshContainer->exMaterials)

// release the textures before deleting the array
if(pMeshContainer->exTextures)
{
for(UINT i = 0; i < pMeshContainer->NumMaterials; ++i)
SAFE_RELEASE(pMeshContainer->exTextures[i]);
}

// texture array
SAFE_DELETE_ARRAY(pMeshContainer->exTextures)

// adjacency data
SAFE_DELETE_ARRAY(pMeshContainer->pAdjacency)

// bone parts
SAFE_DELETE_ARRAY(pMeshContainer->exBoneOffsets)

// frame matrices
SAFE_DELETE_ARRAY(pMeshContainer->exFrameCombinedMatrixPointer)

// release skin mesh
SAFE_RELEASE(pMeshContainer->exSkinMesh)

// release the main mesh
SAFE_RELEASE(pMeshContainer->MeshData.pMesh)

// release skin information
SAFE_RELEASE(pMeshContainer->pSkinInfo)

// finally delete the mesh container itself
SAFE_DELETE(pMeshContainer);

return S_OK;
}



If no-one will read all this code... I won't blame them :-)

Share this post


Link to post
Share on other sites
ankhd    2304
Hi there, I use this code mostly unchanged and my demos seem to work and close ok. (But I also used the same thing to learn from too, so mm??????..)

Are you sure there is a memory leaks. try not using it and just load a basic mesh and see if the errors still there.

Share this post


Link to post
Share on other sites
John_Idol    126
I tought it was something I changed, but then I tried to pay attention at the demo project (unchanged) output and I was getting the memory leaks as well (with both the default bouncy-thing and the skeleton one).
So I set the breakalloc and when it stopped the last call on the callstack was LoadHierarchyFromX. All of this wit the April SDK.
Now doing the same thing with the November SDK I am getting a different result, when breakalloc breaks the execution it says me the there's no source code available for the current location and shows me disassembly code, and the call stack is different. No idea about what I should do, this is the current call stack:


------->ntdll.dll!7c901230() //breaks on this line
d3d9d.dll!00f4ed3d()
d3d9d.dll!00f4e4cb()
d3d9d.dll!00f4e59b()
d3d9d.dll!00f4e440()
d3d9d.dll!00f64963()
d3d9d.dll!00f4af3f()
d3d9d.dll!00f8dd57()
d3d9d.dll!00f4e440()
d3d9d.dll!00fbe7b7()
d3d9d.dll!00fc85ba()
d3d9d.dll!00fc84b7()
d3d9d.dll!0118a46b()
d3d9d.dll!00fcad2c()
D3DX9d_36.dll!0067589b()
d3d9d.dll!00fbb852()
d3d9d.dll!00fbbe81()
d3d9d.dll!00fbbe44()
d3d9d.dll!00fc89ee()
D3DX9d_36.dll!00676567()




[Edited by - John_Idol on November 29, 2007 6:11:28 AM]

Share this post


Link to post
Share on other sites
John_Idol    126
Quote:
Original post by ankhd
it would be good if someone would reply....
is it or is it not got a problem???????? or is it just a debug thing...


Thanks ;-)

Have you been using the same demo?
Can you try and see if you get memory leaks with the original demo? (on VS2003)

Share this post


Link to post
Share on other sites
John_Idol    126
Some new Info:

the breakAllocId changes if I change the Default .X file to be loaded, no matter what how many models how load after. Obviously the number of unfreeed bytes increases if I load more models. This makes me think the break allocId is registered just the first time, and something goes wrong every time I load an .X. No Memory leaks at all if I load models without animation.

With some of the models I am Actually able to BreakOnAllocId and the debbuger kicks in on this line of my implementation of CMeshHierarchy::CreateMeshContainer:


// When we got the device we caused an internal reference count to be incremented
// So we now need to release it
SAFE_RELEASE(pd3dDevice);

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this