Skip to main content
gamedev.net
🔒 Locked

Tracking down DX memory leaks

Started by CCF Oct 9, 2006 at 11:02 PM 4 replies 1.1k views
Original Post
CCF
CCF
New update: Correction: the leak was caused by DirectX's October 2004 SDK. Installing a newer one fixed the leak. [smile] Update: Looks like the leak originated from the article source. I forgot to use D3D_DEBUG_INFO while compiling the article's code earlier, which had me running in circles looking for the leak elsewhere. Now to figure out the problem.... [totally] Hi there, I've discovered the DX Debug output feature rather late and now I'm having a bit of trouble tracking down the source of the memory leak it's reporting (report attached below). I'm able to isolate the problem to the D3DXLoadMeshHierarchyFromX function. No leaks are reported if the function isn't called or returns a failure. The mesh loading code is from this article: Working with the DirectX .X File Format and Animation in DirectX 9.0 The source from the article compiles fine without any leaks though. Did I overlook something? Or is it possible that the leak is occuring somewhere else but is somehow related to the D3DXLoadMeshHierarchyFromX function? Any debugging tips for a newbie? Thank you for your time! [smile] This is the debug output I've got

Direct3D9: (INFO) :MemFini!
Direct3D9: (ERROR) :Memory still allocated!  Alloc count = 117
Direct3D9: (ERROR) :Current Process (pid) = 00000610
Direct3D9: (ERROR) :Memory Address: 00c442bc lAllocID=1 dwSize=000047f8, ReturnAddr=00d734c8 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c4a37c lAllocID=9 dwSize=00000af8, ReturnAddr=00d6c2cb (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c42f7c lAllocID=10 dwSize=00000008, ReturnAddr=00d6c36b (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c4aeb4 lAllocID=11 dwSize=00000e20, ReturnAddr=00d71741 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c42e9c lAllocID=12 dwSize=00000044, ReturnAddr=00d734c8 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c4bd0c lAllocID=13 dwSize=00000050, ReturnAddr=00d734c8 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c48d8c lAllocID=22 dwSize=000006bc, ReturnAddr=00d7f3b3 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c49484 lAllocID=24 dwSize=00000ec4, ReturnAddr=00d745bd (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c42f14 lAllocID=26 dwSize=00000008, ReturnAddr=00d746ee (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c4bd94 lAllocID=28 dwSize=00003508, ReturnAddr=00d734c8 (pid=00000610)
Direct3D9: (ERROR) :Memory Address: 00c4f2dc lAllocID=29 dwSize=00000198, ReturnAddr=00d734c8 (pid=00000610)
Direct3D9: (ERROR) :Total Memory Unfreed From Current Process = 44500 bytes





[Edited by - CCF on December 20, 2006 10:30:41 AM]
TheAdmiral
TheAdmiral
The leak could be coming from just about anywhere in the DirectX COM. Make sure you have one Release() call for each object you create. Something as simple as forgetting to Release() a ID3DXFont object can give you a seemingly endless list of leaks on termination.

If all else fails, comment out the guts of your program and introduce functionality a piece at a time. Often you'll find that the object responsible is one that you didn't think even needed releasing.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
sirob
sirob
The DX Control Panel application has a "Break on AllocID" option. Enable it, and fill in the AllocIDs your application fails to release (these are stated in the debug dump you posted). This will then break when the resource with that AllocID is created, providing a think hint to what might be going wrong.

Once you know what it is you're not freeing, finding why is much simpler.

Hope this helps.
[EDIT] Woah, quadruple post.
Sirob Yes.» - status: Work-O-Rama.
CCF
CCF
Thanks The Admiral and sirob! [smile]

I used both the Debug AllocID and bulk code commenting to narrow down the problem. I'm now pretty sure the leak is coming from some code associated to D3DXLoadMeshHierarchyFromX from the article.

Is there a guide that I could refer to that lists the DX interfaces that need to be release/deleted after use? At the moment, I'm only aware something needs to be freed because an article or code that I'm following does it. So I'm a bit lost at what to look for other than performing random trial and error tests. [depressed]

Below's the CAllocateHierarchy.cpp that's associated with the load mesh function. I think the leak is coming from in there. It's pretty long, so I'm not expecting anyone to look through it... just need some pointers to help guide me along. Thanks!

///////////////////////////////////////////////////// File: "CAllocateHierarchy.cpp"//// Author: Jason Jurecka//// Creation Date: June 9, 2003//// Purpose: This is an Allocation class that is//      used with the D3DXLoadMeshHierarchyFromX//      function. It handles the Creation and Deletion//      of Frames and Mesh Containers. The overloaded//      functions are callbacks, so there is no need//      to call any of the functions in written code//      just pass an Object of this class to the function/////////////////////////////////////////////////#include "SMAllocateHierarchy.h"HRESULT CAllocateHierarchy::CreateFrame(LPCTSTR Name, LPD3DXFRAME *ppNewFrame){       // Create a frame using my derived structure here   LPFRAME pFrame = new FRAME;   ZeroMemory(pFrame, sizeof(FRAME));   // Initialize the passed in frame   *ppNewFrame = NULL;   // Put the name in the frame   if(Name)   {      // size_t nNameSize = strlen(Name)+1;      int nNameSize = (int)strlen(Name)+1;      pFrame->Name = new char[nNameSize];      memcpy(pFrame->Name, Name, nNameSize*sizeof(char));   }   else      pFrame->Name = NULL;      // Initialize the rest of the frame   pFrame->pFrameFirstChild = NULL;   pFrame->pFrameSibling = NULL;   pFrame->pMeshContainer = NULL;   D3DXMatrixIdentity(&pFrame->matCombined);   D3DXMatrixIdentity(&pFrame->TransformationMatrix);   // Set the output frame to the one that we have   *ppNewFrame = (LPD3DXFRAME)pFrame;   // It no longer points to the frame   pFrame = NULL;   // Returns an HRESULT so give it the AOk result   return S_OK;}HRESULT CAllocateHierarchy::CreateMeshContainer(LPCTSTR Name,         CONST D3DXMESHDATA *pMeshData,         CONST D3DXMATERIAL *pMaterials,         CONST D3DXEFFECTINSTANCE *pEffectInstances,         DWORD NumMaterials,         CONST DWORD *pAdjacency,         LPD3DXSKININFO pSkinInfo,         LPD3DXMESHCONTAINER *ppNewMeshContainer){   // Create a Temp mesh container using my derived structure   LPMESHCONTAINER pMeshContainer = new MESHCONTAINER;   ZeroMemory(pMeshContainer, sizeof(MESHCONTAINER));   // Initialize passed in Container to NULL   *ppNewMeshContainer = NULL;   if(Name)   {      // Put the name in the mesh container      // size_t nNameSize = strlen(Name)+1;      int nNameSize = (int)strlen(Name)+1;      pMeshContainer->Name = new char[nNameSize];      memcpy(pMeshContainer->Name, Name, nNameSize*sizeof(char));   }   else      pMeshContainer->Name = NULL;   pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;      // Get the number of Faces for adjacency   DWORD dwFaces = pMeshData->pMesh->GetNumFaces();   // Get the number of materials   pMeshContainer->NumMaterials = NumMaterials;   // Create the arrays for the materials and the textures   pMeshContainer->pMaterials9 = new D3DMATERIAL9[pMeshContainer->NumMaterials];   // Multiply by 3 because there are three adjacent triangles   pMeshContainer->pAdjacency = new DWORD[dwFaces*3];   memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * dwFaces*3);      // Get the device to use   LPDIRECT3DDEVICE9 pd3dDevice = NULL; // Direct3D Rendering device   pMeshData->pMesh->GetDevice(&pd3dDevice);   pMeshData->pMesh->CloneMeshFVF(D3DXMESH_MANAGED,       pMeshData->pMesh->GetFVF(), pd3dDevice,       &pMeshContainer->MeshData.pMesh);      pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[NumMaterials];   for(DWORD dw = 0; dw < NumMaterials; ++dw)   {      pMeshContainer->ppTextures[dw] = NULL;      // Added these 2 lines to fix problem with lighting      // Need to copy the material and copy the ambient value to the diffuse value      pMeshContainer->pMaterials9[dw] = pMaterials[dw].MatD3D;      pMeshContainer->pMaterials9[dw].Ambient = pMeshContainer->pMaterials9[dw].Diffuse;      if(pMaterials[dw].pTextureFilename && strlen(pMaterials[dw].pTextureFilename) > 0)      {         if(FAILED(D3DXCreateTextureFromFile(pd3dDevice,             pMaterials[dw].pTextureFilename, &pMeshContainer->ppTextures[dw])))               pMeshContainer->ppTextures[dw] = NULL;      }   }   //Release the device   SAFE_RELEASE(pd3dDevice);   if(pSkinInfo)   {      // first save off the SkinInfo and original mesh data      pMeshContainer->pSkinInfo = pSkinInfo;      pSkinInfo->AddRef();      // Will need an array of offset matrices to move the vertices from       // the figure space to the bone's space      UINT uBones = pSkinInfo->GetNumBones();      pMeshContainer->pBoneOffsets = new D3DXMATRIX[uBones];      //Create the arrays for the bones and the frame matrices      pMeshContainer->ppFrameMatrices = new D3DXMATRIX*[uBones];      // get each of the bone offset matrices so that we don't need to       // get them later      for (UINT i = 0; i < uBones; i++)         pMeshContainer->pBoneOffsets = *(pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(i));   }   else   {      pMeshContainer->pSkinInfo = NULL;      pMeshContainer->pBoneOffsets = NULL;      pMeshContainer->pSkinMesh = NULL;      pMeshContainer->ppFrameMatrices = NULL;   }   pMeshContainer->pMaterials = NULL;   pMeshContainer->pEffects = NULL;   //pMeshContainer->MeshData.pMesh->OptimizeInplace(   //   D3DXMESHOPT_VERTEXCACHE|D3DXMESHOPT_COMPACT|D3DXMESHOPT_ATTRSORT,   //   pMeshContainer->pAdjacency,NULL,NULL,NULL);   // Set the output mesh container to the temp one   *ppNewMeshContainer = pMeshContainer;    pMeshContainer = NULL;   // Returns an HRESULT so give it the AOk result    return S_OK;}HRESULT CAllocateHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree) {   //Convert the frame to the derived structure   LPFRAME pFrame = (LPFRAME)pFrameToFree;   // Delete the name   SAFE_DELETE_ARRAY(pFrame->Name)      // Delete the frame    SAFE_DELETE(pFrame)   // Returns an HRESULT so give it the AOk result    return S_OK; }HRESULT CAllocateHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase){   //Convert to my derived struct type    LPMESHCONTAINER pMeshContainer = (LPMESHCONTAINER)pMeshContainerBase;      // if there is a name   SAFE_DELETE_ARRAY(pMeshContainer->Name)   //if there are materials   SAFE_DELETE_ARRAY(pMeshContainer->pMaterials9)   //Release the textures   if(pMeshContainer->ppTextures)      for(UINT i = 0; i < pMeshContainer->NumMaterials; ++i)         SAFE_RELEASE(pMeshContainer->ppTextures);   //if there are textures   SAFE_DELETE_ARRAY(pMeshContainer->ppTextures)   // if there is adjacency data   SAFE_DELETE_ARRAY(pMeshContainer->pAdjacency)       // if there are bone parts   SAFE_DELETE_ARRAY(pMeshContainer->pBoneOffsets)      //if there are frame matrices   SAFE_DELETE_ARRAY(pMeshContainer->ppFrameMatrices)      SAFE_DELETE_ARRAY(pMeshContainer->pAttributeTable)      //if there is a copy of the mesh here   SAFE_RELEASE(pMeshContainer->pSkinMesh)      //if there is a mesh   SAFE_RELEASE(pMeshContainer->MeshData.pMesh)      // if there is skin information   SAFE_RELEASE(pMeshContainer->pSkinInfo)      //Delete the mesh container   SAFE_DELETE(pMeshContainer)      // Returns an HRESULT so give it the AOk result   return S_OK;}
TheAdmiral
TheAdmiral
I only took a cursory glance, but I noticed you call pSkinInfo->AddRef(). There are very few situations when you need to increase an object's reference counter manually, and whenever you do, you should match it with a call to Release() (which I didn't see anywhere). If the program comes to an end and *pSkinInfo has a nonzero reference count, it won't have been destroyed correctly and a leak will occur. Maybe I have the wrong end of the stick though.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
CCF
CCF
Thanks for the heads up! [smile]

I've tried commenting out the codes around pSkinInfo->AddRef() and also tried adding pSkinInfo->Release() at the end of the If/else condition.

Unfortunately, it still leaks after it's been commented out and calling the Release() produces an error in the code elsewhere. So I guess the leak is happening somewhere else.

Thanks again, I'll try to slowly hunt it down in the next few days.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.