Skip to content

Commit e747d57

Browse files
committed
Fixed memory leak when changing player model
Conflicts: MTA10/mods/deathmatch/logic/CClientGame.cpp MTA10/mods/deathmatch/logic/CClientGame.h
1 parent 04e4037 commit e747d57

15 files changed

+188
-3
lines changed

MTA10/core/CModelCacheManager.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class CModelCacheManagerImpl : public CModelCacheManager
3939
// CModelCacheManager interface
4040
virtual void DoPulse ( void );
4141
virtual void GetStats ( SModelCacheStats& outStats );
42+
virtual bool UnloadModel ( ushort usModelId );
4243
virtual void OnRestreamModel ( ushort usModelId );
4344
virtual void OnClientClose ( void );
4445
virtual void UpdatePedModelCaching ( const std::map < ushort, float >& newNeedCacheList );
@@ -479,6 +480,32 @@ void CModelCacheManagerImpl::SubModelRefCount ( ushort usModelId )
479480
}
480481

481482

483+
///////////////////////////////////////////////////////////////
484+
//
485+
// CModelCacheManagerImpl::UnloadModel
486+
//
487+
// Remove model and associated txd from memory
488+
//
489+
///////////////////////////////////////////////////////////////
490+
bool CModelCacheManagerImpl::UnloadModel ( ushort usModelId )
491+
{
492+
// Stream out usages in the client module
493+
CClientBase* pClientBase = CModManager::GetSingleton().GetCurrentMod();
494+
if ( pClientBase )
495+
pClientBase->RestreamModel( usModelId );
496+
497+
// Uncache usages here
498+
OnRestreamModel( usModelId );
499+
500+
// Ensure model and txd are removed
501+
CModelInfo* pModelInfo = m_pGame->GetModelInfo( usModelId );
502+
if ( pModelInfo )
503+
return pModelInfo->ForceUnload();
504+
505+
return false;
506+
}
507+
508+
482509
///////////////////////////////////////////////////////////////
483510
//
484511
// CModelCacheManagerImpl::OnRestreamModel

MTA10/core/CModelCacheManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class CModelCacheManager
1818
// CModelCacheManager interface
1919
virtual void DoPulse ( void ) = 0;
2020
virtual void GetStats ( SModelCacheStats& outStats ) = 0;
21+
virtual bool UnloadModel ( ushort usModelId ) = 0;
2122
virtual void OnRestreamModel ( ushort usModelId ) = 0;
2223
virtual void OnClientClose ( void ) = 0;
2324
virtual void UpdatePedModelCaching ( const std::map < ushort, float >& newNeedCacheList ) = 0;

MTA10/game_sa/CModelInfoSA.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,6 @@ int CModelInfoSA::GetRefCount ()
758758

759759
void CModelInfoSA::RemoveRef ( bool bRemoveExtraGTARef )
760760
{
761-
assert ( m_dwReferences > 0 );
762-
763761
// Decrement the references
764762
if ( m_dwReferences > 0 )
765763
m_dwReferences--;
@@ -1383,4 +1381,50 @@ void CModelInfoSA::ResetSupportedUpgrades ( void )
13831381
eModelInfoType CModelInfoSA::GetModelType(void)
13841382
{
13851383
return ((eModelInfoType(*)(void))m_pInterface->VFTBL->GetModelType)();
1386-
}
1384+
}
1385+
1386+
//////////////////////////////////////////////////////////////////////////////////////////
1387+
//
1388+
// CModelInfoSA::ForceUnload
1389+
//
1390+
// 1. Unload the dff from memory
1391+
// 2. Unload the associated txd from memory
1392+
// 3. Cross fingers
1393+
//
1394+
// Why do we do this?
1395+
// Player model adds (seemingly) unnecessary refs
1396+
// (Will crash if anything is actually using the model or txd)
1397+
//
1398+
// Won't work if there is a custom model replacement
1399+
//
1400+
// Returns true if model was unloaded
1401+
//
1402+
//////////////////////////////////////////////////////////////////////////////////////////
1403+
bool CModelInfoSA::ForceUnload( void )
1404+
{
1405+
CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface();
1406+
1407+
// Need to have at least one ref to delete pRwObject
1408+
if ( pModelInfoSAInterface->usNumberOfRefs == 0 && pModelInfoSAInterface->pRwObject != NULL )
1409+
{
1410+
pModelInfoSAInterface->usNumberOfRefs++;
1411+
}
1412+
1413+
// Keep removing refs from the model until is it gone
1414+
uint uiLimit = 100;
1415+
while( pModelInfoSAInterface->usNumberOfRefs > 0 && uiLimit-- )
1416+
{
1417+
RemoveRef();
1418+
}
1419+
1420+
// Did it work?
1421+
if ( pModelInfoSAInterface->usNumberOfRefs > 0 || pModelInfoSAInterface->pRwObject != NULL )
1422+
return false;
1423+
1424+
// If success, then remove txd
1425+
ushort usTxdId = GetTextureDictionaryID();
1426+
if ( usTxdId )
1427+
pGame->GetRenderWare()->TxdForceUnload( usTxdId, true );
1428+
1429+
return true;
1430+
}

MTA10/game_sa/CModelInfoSA.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ class CModelInfoSA : public CModelInfo
322322
void ModelAddRef ( EModelRequestType requestType, const char* szTag );
323323
int GetRefCount ( void );
324324
void RemoveRef ( bool bRemoveExtraGTARef = false );
325+
bool ForceUnload ( void );
325326

326327
// CVehicleModelInfo specific
327328
short GetAvailableVehicleMod ( unsigned short usSlot );

MTA10/game_sa/CRenderWareSA.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,54 @@ bool CRenderWareSA::RwTexDictionaryContainsTexture ( RwTexDictionary* pTXD, RwTe
636636
}
637637

638638

639+
////////////////////////////////////////////////////////////////
640+
//
641+
// CRenderWareSA::TxdForceUnload
642+
//
643+
// 1. Unload txd from memory
644+
// 2. Cross fingers
645+
//
646+
// Why do we do this?
647+
// Player model adds (seemingly) unnecessary refs
648+
// (Will crash if anything is actually using the txd)
649+
//
650+
// No idea what will happen if there is a custom txd replacement
651+
//
652+
////////////////////////////////////////////////////////////////
653+
void CRenderWareSA::TxdForceUnload( ushort usTxdId, bool bDestroyTextures )
654+
{
655+
RwTexDictionary* pTxd = CTxdStore_GetTxd( usTxdId );
656+
if ( !pTxd )
657+
return;
658+
659+
// We can abandon the textures instead of destroy. It might be safer, but will cause a memory leak
660+
if ( bDestroyTextures )
661+
{
662+
// Unref the textures
663+
std::vector < RwTexture* > textureList;
664+
pGame->GetRenderWareSA()->GetTxdTextures( textureList, pTxd );
665+
for ( std::vector < RwTexture* > ::iterator iter = textureList.begin() ; iter != textureList.end() ; iter++ )
666+
{
667+
RwTexture* pTexture = *iter;
668+
while( pTexture->refs > 1 )
669+
RwTextureDestroy( pTexture );
670+
RwTextureDestroy( pTexture );
671+
}
672+
}
673+
674+
// Need to have at least one ref for RemoveRef to work correctly
675+
if ( CTxdStore_GetNumRefs( usTxdId ) == 0 )
676+
{
677+
CTxdStore_AddRef( usTxdId );
678+
}
679+
680+
while( CTxdStore_GetNumRefs( usTxdId ) > 0 )
681+
{
682+
CTxdStore_RemoveRef( usTxdId );
683+
}
684+
}
685+
686+
639687
////////////////////////////////////////////////////////////////
640688
//
641689
// CRenderWareSA::GetTXDIDForModelID

MTA10/game_sa/CRenderWareSA.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class CRenderWareSA : public CRenderWare
106106
void RemoveClientEntityRefs ( CClientEntityBase* pClientEntity );
107107
void RemoveShaderRefs ( CSHADERDUMMY* pShaderItem );
108108
bool RightSizeTxd ( const SString& strInTxdFilename, const SString& strOutTxdFilename, uint uiSizeLimit );
109+
void TxdForceUnload ( ushort usTxdId, bool bDestroyTextures );
109110

110111
// CRenderWareSA methods
111112
RwTexture* RightSizeTexture ( RwTexture* pTexture, uint uiSizeLimit );

MTA10/game_sa/gamesa_renderware.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ typedef bool (__cdecl *CTxdStore_LoadTxd_t) (unsigne
196196
typedef void (__cdecl *CTxdStore_RemoveTxd_t) (unsigned int id);
197197
typedef void (__cdecl *CTxdStore_RemoveRef_t) (unsigned int id);
198198
typedef void (__cdecl *CTxdStore_AddRef_t) (unsigned int id);
199+
typedef int (__cdecl *CTxdStore_GetNumRefs_t) (unsigned int id);
199200
typedef RwTexDictionary* (__cdecl *CTxdStore_GetTxd_t) (unsigned int id);
200201
typedef RwTexture* (__cdecl *CClothesBuilder_CopyTexture_t) (RwTexture *texture);
201202

@@ -210,6 +211,7 @@ RWFUNC ( CTxdStore_GetTxd_t CTxdStore_GetTxd , (CTxd
210211
RWFUNC ( CTxdStore_RemoveTxd_t CTxdStore_RemoveTxd , (CTxdStore_RemoveTxd_t) 0xDEAD )
211212
RWFUNC ( CTxdStore_RemoveRef_t CTxdStore_RemoveRef , (CTxdStore_RemoveRef_t) 0xDEAD )
212213
RWFUNC ( CTxdStore_AddRef_t CTxdStore_AddRef , (CTxdStore_AddRef_t) 0xDEAD )
214+
RWFUNC ( CTxdStore_GetNumRefs_t CTxdStore_GetNumRefs , (CTxdStore_GetNumRefs_t) 0xDEAD )
213215
RWFUNC ( CClothesBuilder_CopyTexture_t CClothesBuilder_CopyTexture , (CClothesBuilder_CopyTexture_t) 0xDEAD )
214216

215217
/*****************************************************************************/

MTA10/game_sa/gamesa_renderware.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ void InitRwFunctions( eGameVersion version )
9898
CTxdStore_RemoveTxd = (CTxdStore_RemoveTxd_t) 0x00731E90;
9999
CTxdStore_RemoveRef = (CTxdStore_RemoveRef_t) 0x00731A30;
100100
CTxdStore_AddRef = (CTxdStore_AddRef_t) 0x00731A00;
101+
CTxdStore_GetNumRefs = (CTxdStore_GetNumRefs_t) 0x00731AA0;
101102
CClothesBuilder_CopyTexture = (CClothesBuilder_CopyTexture_t) 0x005A5730;
102103

103104
break;
@@ -187,6 +188,7 @@ void InitRwFunctions( eGameVersion version )
187188
CTxdStore_RemoveTxd = (CTxdStore_RemoveTxd_t) 0x00731E90;
188189
CTxdStore_RemoveRef = (CTxdStore_RemoveRef_t) 0x00731A30;
189190
CTxdStore_AddRef = (CTxdStore_AddRef_t) 0x00731A00;
191+
CTxdStore_GetNumRefs = (CTxdStore_GetNumRefs_t) 0x00731AA0;
190192
CClothesBuilder_CopyTexture = (CClothesBuilder_CopyTexture_t) 0x005A5730;
191193

192194
break;

MTA10/mods/deathmatch/CClient.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ bool CClient::ProcessCommand ( const char* szCommandLine )
268268
}
269269

270270

271+
void CClient::RestreamModel ( unsigned short usModel )
272+
{
273+
if ( g_pClientGame )
274+
{
275+
g_pClientGame->RestreamModel( usModel );
276+
}
277+
}
278+
279+
271280
bool CClient::HandleException ( CExceptionInformation* pExceptionInformation )
272281
{
273282
#ifndef MTA_DEBUG

MTA10/mods/deathmatch/CClient.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class CClient : public CClientBase
3030
void PreHUDRenderExecutionHandler ( bool bDidUnminimize, bool bDidRecreateRenderTargets );
3131
void PostFrameExecutionHandler ( void );
3232
void IdleHandler ( void );
33+
void RestreamModel ( unsigned short usModel );
3334

3435
bool ProcessCommand ( const char* szCommandLine );
3536
//bool ProcessInput ( CInputMessage* pInputMessage );

0 commit comments

Comments
 (0)