Skip to content

Commit 067fc73

Browse files
committed
Rework TMemory::Alloc
1 parent a608025 commit 067fc73

File tree

3 files changed

+120
-159
lines changed

3 files changed

+120
-159
lines changed

Toshi/Source/Core/TMemory.cpp

Lines changed: 95 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
# include "Profiler/tracy/Tracy.hpp"
1919
#endif // TOSHI_PROFILER_MEMORY
2020

21+
#ifdef __SANITIZE_ADDRESS__
22+
# include <sanitizer/asan_interface.h>
23+
#endif
24+
2125
#include "TMemoryDebugOff.h"
2226

2327
/**
@@ -160,39 +164,6 @@ TMemory::~TMemory()
160164
g_pMemory = TNULL;
161165
}
162166

163-
// General utility macro
164-
# define PP_CAT( A, B ) A##B
165-
# define PP_EXPAND( ... ) __VA_ARGS__
166-
167-
// Macro overloading feature support
168-
# define PP_VA_ARG_SIZE( ... ) PP_EXPAND( PP_APPLY_ARG_N( ( PP_ZERO_ARGS_DETECT( __VA_ARGS__ ), PP_RSEQ_N ) ) )
169-
170-
# define PP_ZERO_ARGS_DETECT( ... ) PP_EXPAND( PP_ZERO_ARGS_DETECT_PREFIX_##__VA_ARGS__##_ZERO_ARGS_DETECT_SUFFIX )
171-
# define PP_ZERO_ARGS_DETECT_PREFIX__ZERO_ARGS_DETECT_SUFFIX , , , , , , , , , , , 0
172-
173-
# define PP_APPLY_ARG_N( ARGS ) PP_EXPAND( PP_ARG_N ARGS )
174-
# define PP_ARG_N( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, N, ... ) N
175-
# define PP_RSEQ_N 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
176-
177-
# define PP_OVERLOAD_SELECT( NAME, NUM ) PP_CAT( NAME##_, NUM )
178-
# define PP_MACRO_OVERLOAD( NAME, ... ) PP_OVERLOAD_SELECT( NAME, PP_VA_ARG_SIZE( __VA_ARGS__ ) )( __VA_ARGS__ )
179-
180-
# define CALL_THIS_4( ADDR, TYPE, RET_TYPE, THIS ) ( ( RET_TYPE( __thiscall* )( TYPE pThis ) )( ADDR ) )( THIS )
181-
# define CALL_THIS_6( ADDR, TYPE, RET_TYPE, THIS, TYPE1, VALUE1 ) ( ( RET_TYPE( __thiscall* )( TYPE, TYPE1 ) )( ADDR ) )( THIS, VALUE1 )
182-
# define CALL_THIS_8( ADDR, TYPE, RET_TYPE, THIS, TYPE1, VALUE1, TYPE2, VALUE2 ) ( ( RET_TYPE( __thiscall* )( TYPE, TYPE1, TYPE2 ) )( ADDR ) )( THIS, VALUE1, VALUE2 )
183-
# define CALL_THIS_10( ADDR, TYPE, RET_TYPE, THIS, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3 ) ( ( RET_TYPE( __thiscall* )( TYPE, TYPE1, TYPE2, TYPE3 ) )( ADDR ) )( THIS, VALUE1, VALUE2, VALUE3 )
184-
# define CALL_THIS_12( ADDR, TYPE, RET_TYPE, THIS, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4 ) ( ( RET_TYPE( __thiscall* )( TYPE, TYPE1, TYPE2, TYPE3, TYPE4 ) )( ADDR ) )( THIS, VALUE1, VALUE2, VALUE3, VALUE4 )
185-
# define CALL_THIS_14( ADDR, TYPE, RET_TYPE, THIS, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4, TYPE5, VALUE5 ) ( ( RET_TYPE( __thiscall* )( TYPE, TYPE1, TYPE2, TYPE3, TYPE4, TYPE5 ) )( ADDR ) )( THIS, VALUE1, VALUE2, VALUE3, VALUE4, VALUE5 )
186-
# define CALL_THIS( ... ) PP_MACRO_OVERLOAD( CALL_THIS, __VA_ARGS__ )
187-
188-
# define CALL_2( ADDR, RET_TYPE ) ( ( RET_TYPE( __stdcall* )() )( ADDR ) )()
189-
# define CALL_4( ADDR, RET_TYPE, TYPE1, VALUE1 ) ( ( RET_TYPE( __stdcall* )( TYPE1 ) )( ADDR ) )( VALUE1 )
190-
# define CALL_6( ADDR, RET_TYPE, TYPE1, VALUE1, TYPE2, VALUE2 ) ( ( RET_TYPE( __stdcall* )( TYPE1, TYPE2 ) )( ADDR ) )( VALUE1, VALUE2 )
191-
# define CALL_8( ADDR, RET_TYPE, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3 ) ( ( RET_TYPE( __stdcall* )( TYPE1, TYPE2, TYPE3 ) )( ADDR ) )( VALUE1, VALUE2, VALUE3 )
192-
# define CALL_10( ADDR, RET_TYPE, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4 ) ( ( RET_TYPE( __stdcall* )( TYPE1, TYPE2, TYPE3, TYPE4 ) )( ADDR ) )( VALUE1, VALUE2, VALUE3, VALUE4 )
193-
# define CALL_12( ADDR, RET_TYPE, TYPE1, VALUE1, TYPE2, VALUE2, TYPE3, VALUE3, TYPE4, VALUE4, TYPE5, VALUE5 ) ( ( RET_TYPE( __stdcall* )( TYPE1, TYPE2, TYPE3, TYPE4, TYPE5 ) )( ADDR ) )( VALUE1, VALUE2, VALUE3, VALUE4, VALUE5 )
194-
# define CALL( ... ) PP_MACRO_OVERLOAD( CALL, __VA_ARGS__ )
195-
196167
// $Barnyard: FUNCTION 006b5230
197168
/**
198169
* @brief Allocates memory with specified size and alignment
@@ -212,176 +183,141 @@ TMemory::~TMemory()
212183
*/
213184
void* TMemory::Alloc( TSIZE a_uiSize, TSIZE a_uiAlignment, MemBlock* a_pMemBlock, const TCHAR* a_szFileName, TINT a_iLineNum )
214185
{
215-
//return CALL_THIS( 0x006b5230, TMemory*, void*, this, TUINT, a_uiSize, TINT, a_uiAlignment, MemBlock*, a_pMemBlock, const TCHAR*, a_szFileName, TINT, a_iLineNum );
216-
217186
TMUTEX_LOCK_SCOPE( ms_pGlobalMutex );
218187

219-
volatile TSIZE uiSize = a_uiSize;
220-
volatile TSIZE uiAlignment = a_uiAlignment;
221-
MemBlock* pMemBlock = a_pMemBlock;
222-
188+
TSIZE uiSize = a_uiSize;
223189
if ( uiSize < 4 )
224190
uiSize = 4;
225191

192+
TSIZE uiAlignment = a_uiAlignment;
226193
if ( uiAlignment < 16 )
227194
uiAlignment = 16;
228195

196+
MemBlock* pBlock = a_pMemBlock;
197+
if ( !pBlock )
198+
pBlock = this->m_pGlobalBlock;
199+
229200
if ( uiAlignment < TMEMORY_ROUNDUP )
230201
{
231202
TDebug_FinalPrintf( "MEMORY ERROR: CANT ALLOC Alignment(%d)<TMEMORY_ROUNDUP\n", uiAlignment );
232-
DebugPrintHALMemInfo( "Out of Toshi Memory on block [%s]\n", pMemBlock->m_szName );
203+
DebugPrintHALMemInfo( "Out of Toshi Memory on block [%s]\n", pBlock->m_szName );
233204
TASSERT( TFALSE );
234205
return TNULL;
235206
}
236207

237-
if ( !pMemBlock )
238-
{
239-
pMemBlock = m_pGlobalBlock;
240-
}
241-
242-
volatile TSIZE uiAlignedSize = TAlignNumUp( uiSize );
243-
volatile TSIZE uiRequiredSize = uiAlignedSize + sizeof( MemNode );
208+
// Align size up to the nearest 4 bytes boundary
209+
TSIZE alignedSize = TAlignNumUp( uiSize );
210+
TSIZE listIndex = MapSizeToFreeList( alignedSize );
244211

245-
MemNode* pMemNode = TNULL;
212+
TSIZE index = listIndex;
213+
MemNode* hole = TNULL;
214+
TSIZE availableSize = 0;
215+
void* alignedStart = TNULL;
246216

247-
volatile void* pAllocatedAddress;
248-
volatile TUINTPTR uiDataRegionStart;
249-
volatile TUINTPTR uiDataRegionEnd;
250-
volatile TUINTPTR uiDataRegionSize;
251-
252-
volatile TUINT uiNodeId = MapSizeToFreeList( uiAlignedSize );
253-
254-
// Find a hole that can allocate the required number of bytes
255-
for ( ; uiNodeId < TMEMORY_NUM_FREELISTS; uiNodeId++ )
217+
// Find a suitable hole in the free lists
218+
if ( index < TMEMORY_NUM_FREELISTS )
256219
{
257-
pMemNode = pMemBlock->m_apHoles[ uiNodeId ];
258-
259-
while ( pMemNode != TNULL )
220+
while ( index < TMEMORY_NUM_FREELISTS )
260221
{
261-
pAllocatedAddress = TAlignPointerUp( pMemNode->GetDataRegionStart(), uiAlignment );
262-
uiDataRegionStart = TREINTERPRETCAST( TUINTPTR, pAllocatedAddress );
263-
uiDataRegionEnd = TREINTERPRETCAST( TUINTPTR, pMemNode->GetDataRegionEnd() );
264-
uiDataRegionSize = uiDataRegionEnd - uiDataRegionStart;
222+
hole = pBlock->m_apHoles[ index ];
223+
while ( hole )
224+
{
225+
// Align start address based on requested alignment
226+
alignedStart = TAlignPointerUp( hole->GetDataRegionStart(), uiAlignment );
265227

266-
if ( uiDataRegionEnd > uiDataRegionStart && uiDataRegionSize >= uiAlignedSize )
267-
break;
228+
if ( (TUINTPTR)hole->GetDataRegionEnd() > (TUINTPTR)alignedStart )
229+
{
230+
availableSize = (TUINTPTR)hole->GetDataRegionEnd() - (TUINTPTR)alignedStart;
231+
if ( availableSize >= alignedSize )
232+
goto HOLE_FOUND;
233+
}
268234

269-
// This freelist can't be used, let's check for the next
270-
pMemNode = pMemNode->pNextHole;
235+
hole = hole->pNextHole;
236+
}
237+
index++;
271238
}
272-
273-
if ( pMemNode )
274-
break;
275-
}
276-
277-
if ( !pMemNode )
278-
{
279-
DebugPrintHALMemInfo( "Out of Toshi Memory on block [%s]\n", pMemBlock->m_szName );
280-
DebugPrintHALMemInfo( "Requested memory block size: %d\n", uiAlignedSize );
281-
DumpMemInfo();
282-
283-
TASSERT( TFALSE );
284-
return TNULL;
285239
}
286240

287-
# ifdef TOSHI_PROFILER_MEMORY
288-
// Create named zone to make it possible to know where the allocation occured
289-
tracy::ScopedZone zone( TMemory__LINE__, TMemory__FILE__, T2String8::Length( TMemory__FILE__ ), "", 0, TMemory__FUNC__ ? TMemory__FUNC__ : "", TMemory__FUNC__ ? T2String8::Length( TMemory__FUNC__ ) : 0 );
290-
# endif
241+
// Out of memory path
242+
DebugPrintHALMemInfo( "Out of Toshi Memory on block [%s]\n", pBlock->m_szName );
243+
DebugPrintHALMemInfo( "Requested memory block size: %d\n", alignedSize );
244+
DumpMemInfo();
291245

292-
MemNode* pAllocNode = MEM_TO_NODE( pAllocatedAddress );
246+
TASSERT( TFALSE );
247+
return TNULL;
293248

294-
if ( volatile MemNode* pOwner = pMemNode->pOwner )
249+
HOLE_FOUND:
250+
if ( hole->pOwner == TNULL )
295251
{
296-
pOwner->uiSize = ( uiDataRegionStart - (TUINTPTR)pOwner - 24 ) | pOwner->uiSize & TMEMORY_FLAGS_MASK;
252+
MemNode* newNode = MEM_TO_NODE( alignedStart );
253+
if ( newNode != hole )
254+
{
255+
// If this is the root hole and it shifts due to alignment, update the block's tracker
256+
pBlock->m_pFirstHole = newNode;
257+
}
297258
}
298-
else if ( pAllocNode != pMemNode )
259+
else
299260
{
300-
// Seems that due to alignment we have a gap between start of the
301-
// data region and the actual address we gonna return so let's
302-
// make sure we don't lost this pointer
303-
pMemBlock->m_pFirstHole = pAllocNode;
261+
// Adjust the owner's size retaining its original flags (bottom 2 bits), extending it to absorb padding
262+
hole->pOwner->uiSize = ( hole->pOwner->uiSize & TMEMORY_FLAGS_MASK ) | ( (TUINTPTR)alignedStart - (TUINTPTR)hole->pOwner - 24 );
304263
}
305264

306-
// Check if we can split the hole in two
307-
if ( uiDataRegionSize > uiRequiredSize )
308-
{
309-
// We can split it!
310-
311-
// Unlink the hole from the linked list
312-
if ( volatile MemNode* pPrev = pMemNode->pPrevHole )
313-
pPrev->pNextHole = pMemNode->pNextHole;
314-
else
315-
pMemBlock->m_apHoles[ uiNodeId ] = pMemNode->pNextHole;
316-
317-
// Remove reference to this hole from the next one
318-
if ( volatile MemNode* pNext = pMemNode->pNextHole )
319-
pNext->pPrevHole = pMemNode->pPrevHole;
265+
// Disconnect old hole from its free list
266+
if ( hole->pPrevHole )
267+
hole->pPrevHole->pNextHole = hole->pNextHole;
268+
else
269+
pBlock->m_apHoles[ index ] = hole->pNextHole;
320270

321-
if ( pMemNode != pAllocNode )
322-
{
323-
TSIZE uiNodeSize = GetNodeSize( pMemNode );
324-
pAllocNode->pOwner = pMemNode->pOwner;
325-
*(MemNode**)( (TUINTPTR)pMemNode->GetDataRegionStart() + uiNodeSize ) = pAllocNode;
326-
}
271+
if ( hole->pNextHole )
272+
hole->pNextHole->pPrevHole = hole->pPrevHole;
327273

328-
// Create a new hole right after the allocated data
329-
MemNode* pNewNode = TREINTERPRETCAST( MemNode*, uiDataRegionStart + uiAlignedSize );
274+
MemNode* newNode = MEM_TO_NODE( alignedStart );
275+
if ( newNode != hole )
276+
{
277+
MemNode** pBoundaryTag = (MemNode**)hole->GetDataRegionEnd();
278+
MemNode* pCachedOwner = hole->pOwner;
330279

331-
SetProcess( pMemBlock, pAllocNode, uiDataRegionSize );
332-
SetHoleSize( pNewNode, (TUINTPTR)pAllocNode + GetNodeSize( pAllocNode ) - uiDataRegionStart - uiAlignedSize );
333-
SetProcess( pMemBlock, pAllocNode, uiAlignedSize + uiDataRegionStart - (TUINTPTR)pAllocNode - TMEMORY_ALLOC_RESERVED_SIZE );
334-
pNewNode->pOwner = pAllocNode;
280+
newNode->pOwner = pCachedOwner;
281+
*pBoundaryTag = newNode;
282+
}
335283

336-
// Place the new hole in the memblock's list
284+
if ( availableSize > alignedSize + sizeof( MemNode ) )
285+
{
286+
// Split Hole
287+
MemNode* newHole = (MemNode*)( (TUINTPTR)alignedStart + alignedSize );
288+
TSIZE remainingSize = availableSize - ( sizeof( MemNode ) - sizeof( void* ) ) - alignedSize;
337289

338-
TUINT uiNewHoleId = MapSizeToFreeList( GetNodeSize( pNewNode ) );
290+
newHole->uiSize = remainingSize;
291+
newHole->pOwner = newNode;
339292

340-
MemNode* pOldNode = pMemBlock->m_apHoles[ uiNewHoleId ];
341-
pNewNode->pNextHole = pOldNode;
293+
newNode->pMemBlock = pBlock;
294+
newNode->uiSize = alignedSize | m_uiGlobalFlags | 1; // OR '1' flag sets allocation to true
342295

343-
if ( pOldNode )
344-
pOldNode->pPrevHole = pNewNode;
296+
// Insert new remainder hole into appropriate free list
297+
TSIZE newIndex = MapSizeToFreeList( remainingSize - 1 );
298+
newHole->pNextHole = pBlock->m_apHoles[ newIndex ];
345299

346-
pNewNode->pPrevHole = TNULL;
347-
pMemBlock->m_apHoles[ uiNewHoleId ] = pNewNode;
300+
if ( newHole->pNextHole )
301+
newHole->pNextHole->pPrevHole = newHole;
348302

349-
// Save pointer to the hole right at the end of the data region (probably for some validation)
350-
*(MemNode* volatile*)pNewNode->GetDataRegionEnd() = pNewNode;
303+
newHole->pPrevHole = TNULL;
304+
pBlock->m_apHoles[ newIndex ] = newHole;
351305

352-
# ifdef TOSHI_PROFILER_MEMORY
353-
TracyAlloc( (void*)pAllocatedAddress, uiAlignedSize );
354-
# endif
355-
return (void*)pAllocatedAddress;
306+
*(MemNode**)newHole->GetDataRegionEnd() = newHole; // Apply boundary tag
356307
}
357308
else
358309
{
359-
// Damn, we can't split this one but it surely can fit the allocation
360-
361-
// Unlink the hole from the linked list
362-
if ( volatile MemNode* pPrev = pMemNode->pPrevHole )
363-
pPrev->pNextHole = pMemNode->pNextHole;
364-
else
365-
pMemBlock->m_apHoles[ uiNodeId ] = pMemNode->pNextHole;
366-
367-
// Remove reference to this hole from the next one
368-
if ( volatile MemNode* pNext = pMemNode->pNextHole )
369-
pNext->pPrevHole = pMemNode->pPrevHole;
370-
371-
if ( pMemNode != pAllocNode )
372-
{
373-
TSIZE uiNodeSize = GetNodeSize( pMemNode );
374-
pAllocNode->pOwner = pMemNode->pOwner;
375-
*(MemNode* volatile*)( (TUINTPTR)pMemNode->GetDataRegionStart() + uiNodeSize ) = pAllocNode;
376-
}
310+
// Consume Entire Hole
311+
newNode->pMemBlock = pBlock;
312+
newNode->uiSize = availableSize | m_uiGlobalFlags | 1; // Mark node completely occupied
377313

378-
SetProcess( pMemBlock, pAllocNode, uiDataRegionSize );
314+
}
379315

380316
# ifdef TOSHI_PROFILER_MEMORY
381-
TracyAlloc( (void*)pAllocatedAddress, uiAlignedSize );
317+
TracyAlloc( alignedStart, alignedSize );
382318
# endif
383-
return (void*)pAllocatedAddress;
384-
}
319+
320+
return alignedStart;
385321
}
386322

387323
// $Barnyard: FUNCTION 006b4a20
@@ -398,8 +334,6 @@ void* TMemory::Alloc( TSIZE a_uiSize, TSIZE a_uiAlignment, MemBlock* a_pMemBlock
398334
*/
399335
TBOOL TMemory::Free( const void* a_pAllocated )
400336
{
401-
//return CALL_THIS( 0x006b4a20, TMemory*, TBOOL, this, const void*, a_pAllocated );
402-
403337
TMUTEX_LOCK_SCOPE( ms_pGlobalMutex );
404338

405339
if ( !a_pAllocated || !TIsPointerAligned( a_pAllocated ) )
@@ -417,6 +351,10 @@ TBOOL TMemory::Free( const void* a_pAllocated )
417351
TSIZE uiAllocationSize = GetNodeSize( pAllocationNode );
418352
MemBlock* pMemBlock = GetProcessMemBlock( pAllocationNode );
419353

354+
# ifdef __SANITIZE_ADDRESS__
355+
__asan_unpoison_memory_region( a_pAllocated, uiAllocationSize );
356+
# endif
357+
420358
SetHoleSize( pAllocationNode, uiAllocationSize );
421359

422360
MemNode* pNextLyingNode = (MemNode*)pAllocationNode->GetDataRegionEnd();

Toshi/Source/Render/TModel.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ TBOOL TModel::LoadTRB( TTRB* a_pTRB )
124124
// $Barnyard: FUNCTION 006ce820
125125
TBOOL TModel::LoadTRB( const TCHAR* a_szFileName, TTRB* a_pAssetTRB, TUINT8 a_ui8FileNameLen )
126126
{
127+
#ifdef BARNYARD_COMMUNITY_PATCH
128+
129+
if ( m_bIsAssetFile && m_pTRB ) delete[] m_szSymbolPrefix;
130+
131+
#endif // BARNYARD_COMMUNITY_PATCH
132+
127133
if ( !m_bIsAssetFile && m_pTRB )
128134
{
129135
delete m_pTRB;
@@ -147,12 +153,29 @@ TBOOL TModel::LoadTRB( const TCHAR* a_szFileName, TTRB* a_pAssetTRB, TUINT8 a_ui
147153
}
148154
}
149155

156+
#ifdef BARNYARD_COMMUNITY_PATCH
157+
if ( m_bIsAssetFile )
158+
{
159+
// Trying to fix careless memory handling that sometimes causes crashes in the original game
160+
m_szSymbolPrefix = T2String8::CreateCopy( a_szFileName );
161+
}
162+
#endif // BARNYARD_COMMUNITY_PATCH
163+
150164
return LoadTRB();
151165
}
152166

153167
// $Barnyard: FUNCTION 006ce780
154168
void TModel::UnloadTRB()
155169
{
170+
#ifdef BARNYARD_COMMUNITY_PATCH
171+
if ( m_bIsAssetFile )
172+
{
173+
// Patch creates a copy of the symbol string so need to deallocate it here
174+
delete[] m_szSymbolPrefix;
175+
m_szSymbolPrefix = TNULL;
176+
}
177+
#endif // BARNYARD_COMMUNITY_PATCH
178+
156179
if ( m_pTRB )
157180
{
158181
// Reset pointers in collision meshes, since we don't own the data and don't want to free it

Toshi/Source/Render/TModel.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ class TModel
7676
Flags_Loaded = BITFLAG( 1 ),
7777
};
7878

79-
using t_ModelLoaderTRBCallback = TBOOL ( * )( TModel* a_pModel );
80-
using t_ModelLoaderTMDCallback = TBOOL ( * )( TModel* a_pModel );
79+
using t_ModelLoaderTRBCallback = TBOOL( __stdcall* )( TModel* a_pModel );
80+
using t_ModelLoaderTMDCallback = TBOOL( __stdcall* )( TModel* a_pModel );
8181

8282
static constexpr TUINT MAX_NUM_LODS = 5;
8383

0 commit comments

Comments
 (0)