thug/Code/Sys/Mem/memman.cpp
2016-02-14 08:39:12 +11:00

1723 lines
49 KiB
C++

/*****************************************************************************
** **
** Neversoft Entertainment **
** **
** Copyright (C) 1999 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: System Library **
** **
** Module: Memory (Mem) **
** **
** File name: memman.cpp **
** **
** Created by: 03/20/00 - mjb **
** **
** Description: Memory manager **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <malloc.h>
#include <string.h>
#include <core/defines.h>
#include <core/thread.h>
#include <core/string/stringutils.h>
#include "memman.h"
#include <sys\mem\region.h>
#include <sys\config\config.h> // for memory profiling, to see if we ahve the extra memory
#include "heap.h"
#include "alloc.h"
#include <sys/profiler.h>
#ifdef __PLAT_XBOX__
#include <xtl.h>
#endif
#ifdef __PLAT_NGC__
#include <dolphin.h>
#endif
#include <sk/heap_sizes.h>
#ifdef __PLAT_NGPS__
static bool s_use_semaphore = false;
void WaitSemaMaybe(int sema)
{
if (s_use_semaphore)
{
WaitSema(sema);
}
}
void SignalSemaMaybe(int sema)
{
if (s_use_semaphore)
{
SignalSema(sema);
}
}
#endif
/*****************************************************************************
** DBG Information **
*****************************************************************************/
/*****************************************************************************
** Externals **
*****************************************************************************/
#if defined ( __PLAT_NGPS__ )
extern char _code_start[] __attribute__((section(".text")));
extern char _code_end[] __attribute__((section(".text")));
extern char _data_end[] __attribute__((section(".text")));
extern char _mem_start[] __attribute__((section(".mem_block")));
extern char _mem_end[] __attribute__((section(".mem_block")));
extern char _std_mem_end[] __attribute__((section(".mem_block")));
extern char _debug_heap_start[] __attribute__((section(".mem_block")));
extern char _script_heap_start[] __attribute__((section(".mem_block")));
#else
char* _code_start;
char* _code_end;
char* _data_end;
char* _mem_start;
char* _mem_end;
char* _std_mem_end;
char * _debug_heap_start;
#endif
/*****************************************************************************
** Defines **
*****************************************************************************/
namespace Mem
{
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
char Manager::s_manager_buffer[sizeof(Manager)];
char Manager::s_region_buffer[sizeof(Region)];
char Manager::s_script_region_buffer[sizeof(Region)];
char Manager::s_top_heap_buffer[sizeof(Heap)];
char Manager::s_bot_heap_buffer[sizeof(Heap)];
char Manager::s_debug_heap_buffer[sizeof(Heap)];
Manager* Manager::sp_instance = NULL;
#ifdef __PLAT_NGPS__
static int s_context_semaphore;
#endif
char Manager::s_debug_region_buffer[sizeof(Region)];
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
Manager::Manager( void )
{
m_current_id = 0;
mp_process_man = NULL;
# if defined ( __PLAT_XBOX__ )
// Just grab 33mb of main memory.
_mem_start = (char*)XPhysicalAlloc( 33 * 1024 * 1024, // size of region
MAXULONG_PTR, // base physical address
0, // memory alignment
PAGE_READWRITE ); // memory protection and type
_mem_end = _mem_start + ( 33 * 1024 * 1024 );
_std_mem_end = _mem_end;
# elif defined ( __PLAT_WN32__ )
// Nasty hack for WN32 for now - just grab 38mb of main memory via a malloc.
_mem_start = (char *)malloc( 38 * 1024 * 1024 );
_mem_end = _mem_start + ( 38 * 1024 * 1024 );
_std_mem_end = _mem_end;
# endif
# if defined ( __PLAT_NGC__ )
// Fill in what we know.
_code_start = (char *)0xfadebabe; // Junk addresses.
_code_end = (char *)0xfacced00;
_data_end = (char *)0xcaccfacc;
_mem_start = &((char *)OSGetArenaLo())[8192]; // Real, actual useful addresses.
_mem_end = (char *)OSGetArenaHi();
_std_mem_end = (char *)OSGetArenaHi();
# endif
// create root memory region
mp_region = new ((void*)s_region_buffer) Region( nAlignUp( _mem_start ), nAlignDown( _std_mem_end ) );
// create default heaps
mp_top_heap = new ((void*)s_top_heap_buffer) Heap( mp_region, Heap::vTOP_DOWN, "Top Down" );
mp_bot_heap = new ((void*)s_bot_heap_buffer) Heap( mp_region, Heap::vBOTTOM_UP, "BottomUp" );
m_heap_list[0] = mp_top_heap;
m_heap_list[1] = mp_bot_heap;
m_num_heaps = 2;
# if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
uint codesize = ((uint)(_code_end) - (uint)(_code_start))/1024;
uint datasize = ((uint)(_data_end) - (uint)(_code_end))/1024;
printf ( "code [%p - %p] (%dK) + data [%p - %p] (%dK) = %dK \n",
_code_start, _code_end, codesize,
_code_end, _data_end, datasize,
codesize + datasize );
#endif
m_pushed_context_count = 0;
mp_internet_region = NULL;
mp_net_misc_region = NULL;
mp_cutscene_region = NULL;
mp_cutscene_bottom_heap = NULL;
mp_cutscene_top_heap = NULL;
#ifdef __PLAT_NGPS__
// Create a semaphore to prevent threads from re-entering push/pop memory contexts
struct SemaParam params;
params.initCount = 1;
params.maxCount = 10;
s_context_semaphore = CreateSema( &params );
#endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Manager::~Manager( void )
{
mp_top_heap->~Heap();
mp_bot_heap->~Heap();
mp_region->~Region();
#ifdef __PLAT_NGPS__
DeleteSema( s_context_semaphore );
#endif
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
void* Manager::New( size_t size, bool assert_on_fail, Allocator* pAlloc )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
if ( !pAlloc ) // set to 'default' allocator
{
pAlloc = mp_context->mp_alloc;
}
void* p_ret = pAlloc->allocate( size, assert_on_fail );
if ( p_ret ) // if allocation was successful
{
Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle
}
#if 0
if ( mp_process_man )
{
mp_process_man->AllocEv( p_ret, size, pAlloc );
}
#endif
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
return p_ret;
}
// Returns the amount of memory avaialbe in the current context
// currently this is only valid for Heaps
int Manager::Available()
{
return mp_context->mp_alloc->available();
}
// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
void* Manager::ReallocateDown( size_t newSize, void *pOld, Allocator* pAlloc )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
if ( !pAlloc ) // set to 'default' allocator
{
pAlloc = mp_context->mp_alloc;
}
void* p_ret = pAlloc->reallocate_down( newSize, pOld );
if ( p_ret ) // if allocation was successful
{
Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle
}
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
return p_ret;
}
// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
// This is for when the bottoms-up heap is being used.
void *Manager::ReallocateUp( size_t newSize, void *pOld, Allocator* pAlloc )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
if ( !pAlloc ) // set to 'default' allocator
{
pAlloc = mp_context->mp_alloc;
}
void* p_ret = pAlloc->reallocate_up( newSize, pOld );
if ( p_ret ) // if allocation was successful
{
Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle
}
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
return p_ret;
}
void *Manager::ReallocateShrink( size_t newSize, void *pOld, Allocator* pAlloc )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
if ( !pAlloc ) // set to 'default' allocator
{
pAlloc = mp_context->mp_alloc;
}
void* p_ret = pAlloc->reallocate_shrink( newSize, pOld );
if ( p_ret ) // if allocation was successful
{
Allocator::s_set_id( p_ret ); // stamp ID; used by Mem::Handle
}
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
return p_ret;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::PushMemoryMarker( uint32 uiID )
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::PopMemoryMarker( uint32 uiID )
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::Delete( void* pAddr )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
if( pAddr != NULL )
{
Allocator::s_free( pAddr );
#if 0
// 000810 JAB: Modified s_free to return the allocator.
if (mp_process_man)
{
mp_process_man->FreeEv( pAddr, p_alloc );
}
#endif
}
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool Manager::Valid( void* pAddr )
{
if( pAddr != NULL )
{
return Allocator::s_valid( pAddr );
}
else
{
return false;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
size_t Manager::GetAllocSize( void* pAddr )
{
if( pAddr != NULL )
{
return Allocator::s_get_alloc_size( pAddr );
}
else
{
Dbg_MsgAssert( 0, ( "Trying to get the size of an invalid block" ) );
return 0;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::PushContext( Allocator* alloc )
{
Dbg_AssertType ( alloc, Allocator );
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
// printf ("Pushed context %d to %s\n",m_pushed_context_count, alloc->GetName());
// DumpUnwindStack(20,0);
Dbg_MsgAssert(m_pushed_context_count < vMAX_CONTEXT-1,("Pushed too many contexts"));
mp_context = &m_contexts[m_pushed_context_count];
mp_context->mp_alloc = alloc;
m_pushed_context_count++;
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::PopContext( void )
{
#ifdef __PLAT_NGPS__
WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
Dbg_MsgAssert( m_pushed_context_count,( "Heap stack underflow" ));
m_pushed_context_count--;
m_contexts[m_pushed_context_count].mp_alloc = (Mem::Allocator*)-1;
if (m_pushed_context_count)
{
mp_context = &m_contexts[m_pushed_context_count-1];
}
else
{
mp_context = NULL; // stack has now been emptied
}
#ifdef __PLAT_NGPS__
SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
}
//GJ: Context number doesn't seem to be used...
//Originally, it was used so that you could access the heaps from script,
//but now you can access the heaps using names, so the numbers aren't needed
//any more
//int Manager::GetContextNumber()
//{
// return mp_context->mp_alloc->GetNumber();
//}
char * Manager::GetContextName()
{
return mp_context->mp_alloc->GetName();
}
Allocator* Manager::GetContextAllocator()
{
return mp_context->mp_alloc;
}
// Added by Ken, so that pip.cpp knows whether to try and expand a memory block up or down.
Allocator::Direction Manager::GetContextDirection()
{
return mp_context->mp_alloc->GetDirection();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
const char* Manager::GetHeapName( uint32 whichHeap )
{
// first look through the named heaps...
CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap );
if ( pInfo )
{
return pInfo->mp_heap_name;
}
switch ( whichHeap )
{
case 0xc7800b0: // bottomupheap
return "BottomUpHeap";
break;
case 0x8fdb68af: // topdownheap
return "TopDownHeap";
break;
case 0x62f3a0f3: // frontendheap
return "FrontEndHeap";
break;
case 0xe3551d2e: // networkheap
return "NetworkHeap";
break;
case 0x96d29d93: // netmischeap
return "NetMiscHeap";
break;
case 0xfcd5166b: // internettopdownheap
return "InternetTopDownHeap";
break;
case 0x90020867: // internetbottomupheap
return "InternetBottomUpHeap";
break;
case 0xfa33d9b: // scriptheap
return "ScriptHeap";
break;
case 0x70cb0238: // debugheap
return "DebugHeap";
break;
case 0xc3909393: // skaterheap0
return "Skater0";
break;
case 0xb497a305: // skaterheap1
return "Skater1";
break;
case 0x2d9ef2bf: // skaterheap2
return "Skater2";
break;
case 0x5a99c229: // skaterheap3
return "Skater3";
break;
case 0x572a9f4c: // skatergeomheap0
return "SkaterGeom0";
break;
case 0x202dafda: // skatergeomheap1
return "SkaterGeom1";
break;
case 0xb924fe60: // skatergeomheap2
return "SkaterGeom2";
break;
case 0xce23cef6: // skatergeomheap3
return "SkaterGeom3";
break;
case 0x8682d24: // skaterinfoheap
return "SkaterInfo";
break;
case 0x17ecb880: // themeheap
return "ThemeHeap";
break;
case 0x7ed56b49: // CutsceneBottomUpHeap
return "CutsceneBottomUp";
break;
case 0x25d71469: // CutsceneTopDownHeap
return "CutsceneTopDown";
break;
default: // unrecognized heap
Dbg_Assert ( 0 );
}
return "Unrecognized Heap";
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Heap* Manager::GetHeap( uint32 whichHeap )
{
Heap* pHeap = NULL;
// first look through the named heaps...
CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap );
if ( pInfo )
{
return pInfo->mp_heap;
}
switch ( whichHeap )
{
case 0xc7800b0: // bottomupheap
pHeap = BottomUpHeap();
break;
case 0x8fdb68af: // topdownheap
pHeap = TopDownHeap();
break;
case 0x62f3a0f3: // frontendheap
pHeap = FrontEndHeap();
break;
case 0xe3551d2e: // networkheap
pHeap = NetworkHeap();
break;
case 0x96d29d93: // netmischeap
pHeap = NetMiscHeap();
break;
case 0xfcd5166b: // internettopdownheap
pHeap = InternetTopDownHeap();
break;
case 0x90020867: // internetbottomupheap
pHeap = InternetBottomUpHeap();
break;
case 0xfa33d9b: // scriptheap
pHeap = ScriptHeap();
break;
case 0x70cb0238: // debugheap
pHeap = DebugHeap();
break;
case 0xc3909393: // skaterheap0
pHeap = SkaterHeap(0);
break;
case 0xb497a305: // skaterheap1
pHeap = SkaterHeap(1);
break;
case 0x2d9ef2bf: // skaterheap2
pHeap = SkaterHeap(2);
break;
case 0x5a99c229: // skaterheap3
pHeap = SkaterHeap(3);
break;
case 0x572a9f4c: // skatergeomheap0
pHeap = SkaterGeomHeap(0);
break;
case 0x202dafda: // skatergeomheap1
pHeap = SkaterGeomHeap(1);
break;
case 0xb924fe60: // skatergeomheap2
pHeap = SkaterGeomHeap(2);
break;
case 0xce23cef6: // skatergeomheap3
pHeap = SkaterGeomHeap(3);
break;
case 0x8682d24: // skaterinfoheap
pHeap = SkaterInfoHeap();
break;
case 0x17ecb880: // themeheap
pHeap = ThemeHeap();
break;
case 0x7ed56b49: // CutsceneBottomUpHeap
pHeap = CutsceneBottomUpHeap();
break;
case 0x25d71469: // CutsceneTopDownHeap
pHeap = CutsceneTopDownHeap();
break;
default: // unrecognized heap
Dbg_MsgAssert ( 0, ( "Unrecognized heap %08x", whichHeap ) );
}
return pHeap;
}
Mem::Heap *Manager::CreateHeap( Region* region, Mem::Allocator::Direction dir, char* p_name)
{
Mem::Heap * pHeap = new Mem::Heap(region, dir, p_name);
// At this point we can maintain the heap list
m_heap_list[m_num_heaps++] = pHeap;
return pHeap;
}
void Manager::RemoveHeap(Mem::Heap *pHeap)
{
#ifndef __PLAT_WN32__
#ifdef __NOPT_ASSERT__
// Check first to see if there is something on the heap before it is deleted
// Deleting a heap with stuff on it might indicate an error
if (pHeap->mUsedBlocks.m_count)
{
printf ("Deleting a heap %s with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count);
#ifndef __PLAT_NGC__
printf ("\n\nDumping Heap\n");
MemView_DumpHeap(pHeap);
printf ("\n\nAnalyzing Heap\n");
MemView_AnalyzeHeap(pHeap);
Dbg_MsgAssert(0, ("Deleting heap <%s> with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count));
#endif // __PLAT_NGC__
}
#endif
#endif
// remove from list of heaps
for (int i=0;i<m_num_heaps;i++)
{
if (m_heap_list[i] == pHeap)
{
m_num_heaps--; // chop off the last heaps
m_heap_list[i] = m_heap_list[m_num_heaps]; // and insert if in the hole
break;
}
}
delete pHeap;
}
Mem::Heap * Manager::FirstHeap()
{
if (m_num_heaps)
{
return m_heap_list[0];
}
else
{
return NULL;
}
}
Mem::Heap * Manager::NextHeap(Mem::Heap * pHeap)
{
for (int i = 0; i<m_num_heaps-1;i++)
{
if (m_heap_list[i] == pHeap)
{
return m_heap_list[i+1];
}
}
return NULL;
}
#ifdef DEBUG_ADJUSTMENT
static void *p_adjustment;
#endif
void Manager::InitOtherHeaps()
{
if (Config::Bootstrap())
{
mp_frontend_region = new Mem::AllocRegion( (BOOTSTRAP_FRONTEND_HEAP_SIZE) );
}
else
{
mp_frontend_region = new Mem::AllocRegion( (FRONTEND_HEAP_SIZE) );
}
printf ("allocated mp_frontend_region at %p\n",mp_frontend_region);
// Network heap is now a top down heap on the the FE region
// as they are mutually exclusive in their peak usages
// since network heap is only really used in game, when FE usage is small
mp_network_heap = CreateHeap( mp_frontend_region, Mem::Allocator::vTOP_DOWN, "Network" );
printf ("Setup TOP_DOWN mp_network_heap at %p\n",mp_network_heap);
mp_frontend_heap = CreateHeap( mp_frontend_region, Mem::Allocator::vBOTTOM_UP, "FrontEnd" );
printf ("Setup mp_frontend_heap at %p\n",mp_frontend_heap);
#ifdef __PLAT_NGC__
mp_audio_region = new Mem::AllocRegion( (AUDIO_HEAP_SIZE) );
printf ("allocated mp_audio_region at %p\n",mp_audio_region);
mp_audio_heap = CreateHeap( mp_audio_region, Mem::Allocator::vBOTTOM_UP, "Audio" );
printf ("Setup mp_audio_heap at %p\n",mp_audio_heap);
#endif // __PLAT_NGC__
#ifdef DEBUG_ADJUSTMENT
// allocate the script region off the debug heap
// and then allocate a block of size (SCRIPT_HEAP_SIZE-DEBUG_ADJUSTMENT)
// to bring regular memory usage back into line with non-debug builds
mp_script_region = new ((void*)s_script_region_buffer) Region( nAlignUp( _script_heap_start ), nAlignDown( _script_heap_start+SCRIPT_HEAP_SIZE ) );
printf ("allocated mp_script_region at %p\n",mp_script_region);
mp_script_heap = CreateHeap( mp_script_region, Mem::Allocator::vBOTTOM_UP, "Script" );
printf ("Setup mp_script_heap at %p\n",mp_script_heap);
// and allocate the extra memory of the regular heap and forget about it
p_adjustment = Mem::Malloc(SCRIPT_HEAP_SIZE-DEBUG_ADJUSTMENT);
#else
mp_script_region = new Mem::AllocRegion( (SCRIPT_HEAP_SIZE) );
printf ("allocated mp_script_region at %p\n",mp_script_region);
mp_script_heap = CreateHeap( mp_script_region, Mem::Allocator::vBOTTOM_UP, "Script" );
printf ("Setup mp_script_heap at %p\n",mp_script_heap);
#endif
#ifdef __USE_PROFILER__
mp_profiler_region = new Mem::AllocRegion( (PROFILER_HEAP_SIZE) );
printf ("allocated mp_profiler_region at %p\n",mp_profiler_region);
mp_profiler_heap = CreateHeap( mp_profiler_region, Mem::Allocator::vBOTTOM_UP, "profiler" );
printf ("Setup mp_profiler_heap at %p\n",mp_profiler_heap);
#endif
mp_skater_info_region = new Mem::AllocRegion( (SKATERINFO_HEAP_SIZE) );
printf ("allocated mp_skater_info_region at %p\n",mp_skater_info_region);
mp_skater_info_heap = CreateHeap( mp_skater_info_region, Mem::Allocator::vBOTTOM_UP, "skt_info" );
printf ("Setup mp_skater_info_heap at %p\n",mp_skater_info_heap);
mp_theme_region = new Mem::AllocRegion( (THEME_HEAP_SIZE) );
printf ("allocated mp_theme_region at %p\n",mp_theme_region);
mp_theme_heap = CreateHeap( mp_theme_region, Mem::Allocator::vBOTTOM_UP, "theme" );
printf ("Setup mp_theme_heap at %p\n",mp_theme_heap);
// Allocate the permanent skater heap(s)
for (int i = 0; i< NUM_PERM_SKATER_HEAPS; i++)
{
mp_skater_region[i] = new Mem::AllocRegion( (SKATER_HEAP_SIZE) );
printf ("allocated mp_skater_region at %p\n",mp_skater_region[i]);
mp_skater_heap[i] = CreateHeap( mp_skater_region[i], Mem::Allocator::vBOTTOM_UP, "skater" );
printf ("Setup mp_skater_heap at %p\n",mp_skater_heap[i]);
mp_skater_geom_region[i] = new Mem::AllocRegion( (SKATER_GEOM_HEAP_SIZE) );
printf ("allocated mp_skater_geom_region at %p\n",mp_skater_geom_region[i]);
mp_skater_geom_heap[i] = CreateHeap( mp_skater_geom_region[i], Mem::Allocator::vBOTTOM_UP, "skater_geom" );
printf ("Setup mp_skater_geom_heap at %p\n",mp_skater_geom_heap[i]);
}
// clear other temp heaps to null
for (int heap = NUM_PERM_SKATER_HEAPS; heap < NUM_SKATER_HEAPS; heap++)
{
mp_skater_region[heap] = NULL;
mp_skater_heap[heap] = NULL;
mp_skater_geom_region[heap] = NULL;
mp_skater_geom_heap[heap] = NULL;
}
}
void Manager::InitNetMiscHeap()
{
if( mp_net_misc_region == NULL )
{
//#ifndef __NOPT_ASSERT__
#if 1 // always allocate internet heap normally
// normally the internet heap just goes on the top down heap
sp_instance->PushContext( sp_instance->mp_bot_heap );
#else
// but with assertions there is not enough room, so put it on the debug heap
if (Config::GotExtraMemory())
{
sp_instance->PushContext( sp_instance->mp_debug_heap );
}
else
{
// running on a regular PS2, allow them to try, but will probably fail later with out of memory
sp_instance->PushContext( sp_instance->mp_bot_heap );
}
#endif
mp_net_misc_region = new Mem::AllocRegion( (NETMISC_HEAP_SIZE) );
mp_net_misc_heap = CreateHeap( mp_net_misc_region, Mem::Allocator::vBOTTOM_UP, "NetMisc" );
sp_instance->PopContext();
}
}
void Manager::DeleteNetMiscHeap()
{
if( mp_net_misc_region )
{
RemoveHeap( mp_net_misc_heap );
delete mp_net_misc_region;
mp_net_misc_region = NULL;
}
}
void Manager::InitInternetHeap()
{
if( mp_internet_region == NULL )
{
//#ifndef __NOPT_ASSERT__
#if 1 // always allocate internet heap normally
// normally the internet heap just goes on the top down heap
sp_instance->PushContext( sp_instance->mp_bot_heap );
#else
// but with assertions there is not enough room, so put it on the debug heap
if (Config::GotExtraMemory())
{
sp_instance->PushContext( sp_instance->mp_debug_heap );
}
else
{
// running on a regular PS2, allow them to try, but will probably fail later with out of memory
sp_instance->PushContext( sp_instance->mp_bot_heap );
}
#endif
mp_internet_region = new Mem::AllocRegion( (INTERNET_HEAP_SIZE) );
mp_internet_top_heap = CreateHeap( mp_internet_region, Mem::Allocator::vTOP_DOWN, "InternetTopDown" );
mp_internet_bottom_heap = CreateHeap( mp_internet_region, Mem::Allocator::vBOTTOM_UP, "InternetBottomUp" );
sp_instance->PopContext();
}
}
void Manager::DeleteInternetHeap()
{
if( mp_internet_region )
{
RemoveHeap( mp_internet_top_heap );
RemoveHeap( mp_internet_bottom_heap );
delete mp_internet_region;
mp_internet_region = NULL;
}
}
void Manager::InitCutsceneHeap( int heap_size )
{
// Note that it will create the cutscene heap on the current mem context...
if ( mp_cutscene_region == NULL )
{
// put it on the top-down heap, because it's used only temporarily
// sp_instance->PushContext( sp_instance->mp_top_heap );
mp_cutscene_region = new Mem::AllocRegion( heap_size );
Dbg_MsgAssert( mp_cutscene_bottom_heap == NULL, ( "CutsceneBottomUpHeap already exists" ) );
mp_cutscene_bottom_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vBOTTOM_UP, "CutsceneBottomUp" );
Dbg_MsgAssert( mp_cutscene_top_heap == NULL, ( "CutsceneTopDownHeap already exists" ) );
mp_cutscene_top_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vTOP_DOWN, "CutsceneTopDown" );
// sp_instance->PopContext();
}
}
void Manager::DeleteCutsceneHeap()
{
if ( mp_cutscene_region )
{
RemoveHeap( mp_cutscene_bottom_heap );
mp_cutscene_bottom_heap = NULL;
RemoveHeap( mp_cutscene_top_heap );
mp_cutscene_top_heap = NULL;
delete mp_cutscene_region;
mp_cutscene_region = NULL;
}
}
void Manager::InitDebugHeap()
{
#ifdef __PLAT_NGPS__
// The Debug heap is allocated directly from debug memory (>32MB on PS2)
// as such, it should only ever be used on the TOOL (T10K) debug stations, or equivalents on other platforms
mp_debug_region = new ((void*)s_debug_region_buffer) Region( nAlignUp( _debug_heap_start ), nAlignDown( _debug_heap_start+DEBUG_HEAP_SIZE ) );
mp_debug_heap = CreateHeap( mp_debug_region, Mem::Allocator::vBOTTOM_UP, "debug" );
#endif
}
void Manager::InitSkaterHeaps(int players)
{
printf ("Init Skater Heaps\n");
// some game modes specify 8 players, however 4 of those
// are observers ???
// anyway, for now, just don't allow them to create more heaps than the max
// it will assert later if you try to access a heap that has not been created
if (players > NUM_SKATER_HEAPS)
{
players = NUM_SKATER_HEAPS;
}
// Initialize the other skater heaps
for (int heap = NUM_PERM_SKATER_HEAPS; heap < players; heap++)
{
Dbg_MsgAssert(!mp_skater_region[heap],( "Skater region %d is still active!!!\n",heap));
Dbg_MsgAssert(!mp_skater_heap[heap],( "Skater heap %d is still active!!!\n",heap));
mp_skater_region[heap] = new Mem::AllocRegion( (SKATER_HEAP_SIZE) );
printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_region[heap]);
mp_skater_heap[heap] = CreateHeap( mp_skater_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterX" );
printf ("EXTRA: Setup mp_skater_heap at %p\n",mp_skater_heap[heap]);
Dbg_MsgAssert(!mp_skater_geom_region[heap],( "Skater geom region %d is still active!!!\n",heap));
Dbg_MsgAssert(!mp_skater_geom_heap[heap],( "Skater geom heap %d is still active!!!\n",heap));
mp_skater_geom_region[heap] = new Mem::AllocRegion( (SKATER_GEOM_HEAP_SIZE) );
printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_geom_region[heap]);
mp_skater_geom_heap[heap] = CreateHeap( mp_skater_geom_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterGeomX" );
printf ("EXTRA: Setup mp_skater_geom_heap at %p\n",mp_skater_geom_heap[heap]);
}
printf ("END Init Skater Heaps\n");
}
// Delete the temporary skater heaps
void Manager::DeleteSkaterHeaps()
{
for (int i=NUM_PERM_SKATER_HEAPS;i<NUM_SKATER_HEAPS;i++)
{
if (mp_skater_region[i])
{
printf ("DELETING SKATER HEAP %d\n",i);
RemoveHeap(mp_skater_heap[i]);
delete mp_skater_region[i];
mp_skater_heap[i] = NULL;
mp_skater_region[i] = NULL;
}
if (mp_skater_geom_region[i])
{
printf ("DELETING SKATER GEOM HEAP %d\n",i);
RemoveHeap(mp_skater_geom_heap[i]);
delete mp_skater_geom_region[i];
mp_skater_geom_heap[i] = NULL;
mp_skater_geom_region[i] = NULL;
}
}
}
void Manager::DeleteOtherHeaps()
{
RemoveHeap(mp_frontend_heap);
delete mp_frontend_region;
RemoveHeap(mp_script_heap);
delete mp_script_region;
RemoveHeap(mp_network_heap);
delete mp_network_region;
#ifdef __USE_PROFILER__
RemoveHeap(mp_profiler_heap);
delete mp_debug_region;
#endif
RemoveHeap(mp_skater_info_heap);
delete mp_skater_info_region;
// Deallocate the permanent skater heap(s)
for (int i = 0; i< NUM_PERM_SKATER_HEAPS; i++)
{
RemoveHeap(mp_skater_heap[i]);
delete mp_skater_region[i];
RemoveHeap(mp_skater_geom_heap[i]);
delete mp_skater_geom_region[i];
}
DeleteSkaterHeaps(); // just in case we did some preemptive exit
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::sSetUp( void )
{
if ( !sp_instance )
{
sp_instance = new ((void*)s_manager_buffer) Manager;
sp_instance->PushContext( sp_instance->mp_bot_heap ); // make bottom-up heap default
// sp_instance->InitOtherHeaps();
}
else
{
Dbg_Warning( "Already Initialized!" );
}
}
// K: Separated this out from sSetUp because this needs to be called from main(), after
// the config manager has initialised, because it must only be called if extra memory is
// available, and we only know that after Config::Init has been called.
// sSetUp is called from pre_main, and the config manager cannot be called from there
// because it needs the command line args.
void Manager::sSetUpDebugHeap( void )
{
if ( sp_instance )
{
sp_instance->InitDebugHeap();
}
else
{
Dbg_MsgAssert(0,("Called sSetUpDebugHeap when mem manager not initialized!"));
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::sCloseDown( void )
{
if ( sp_instance )
{
#ifndef __PLAT_WN32__
sp_instance->DeleteOtherHeaps();
#endif
sp_instance->PopContext();
sp_instance->~Manager();
sp_instance = NULL;
}
else
{
Dbg_Warning( "Not Initialized!" );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::RegisterPcsMemMan( Pcs::Manager* pReg )
{
Dbg_Assert( mp_process_man == NULL ); // should not initialize twice.
mp_process_man = pReg;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CNamedHeapInfo* Manager::find_named_heap_info( uint32 name )
{
for ( int i = 0; i < NUM_NAMED_HEAPS; i++ )
{
if ( m_named_heap_info[i].m_used && m_named_heap_info[i].m_name == name )
{
return &m_named_heap_info[i];
}
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Heap* Manager::NamedHeap( uint32 name, bool assertOnFail )
{
CNamedHeapInfo* pInfo = find_named_heap_info( name );
if ( pInfo )
{
return pInfo->mp_heap;
}
Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x", name ) );
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Manager::InitNamedHeap( uint32 name, uint32 size, const char* pHeapName )
{
CNamedHeapInfo* pNamedHeapInfo = NULL;
for ( int i = 0; i < NUM_NAMED_HEAPS; i++ )
{
if ( !m_named_heap_info[i].m_used )
{
pNamedHeapInfo = &m_named_heap_info[i];
break;
}
}
Dbg_MsgAssert( pNamedHeapInfo, ( "No more free named heaps (Increase NUM_NAMED_HEAPS from %d)!", NUM_NAMED_HEAPS ) );
// set the correct name
pNamedHeapInfo->m_name = name;
Dbg_MsgAssert( strlen(pHeapName) < CNamedHeapInfo::vMAX_HEAP_NAME_LEN, ( "Heap name %s is too long", pHeapName ) );
strcpy( pNamedHeapInfo->mp_heap_name, pHeapName );
Dbg_MsgAssert(!pNamedHeapInfo->mp_region, ( "Named region is still active" ) );
pNamedHeapInfo->mp_region = new Mem::AllocRegion( size );
Dbg_Message( "EXTRA: Allocated pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_region );
Dbg_MsgAssert(!pNamedHeapInfo->mp_heap, ("Named heap is still active"));
pNamedHeapInfo->mp_heap = CreateHeap( pNamedHeapInfo->mp_region,
Mem::Allocator::vBOTTOM_UP,
pNamedHeapInfo->mp_heap_name );
Dbg_Message( "EXTRA: Setup pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_heap );
pNamedHeapInfo->m_used = true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool Manager::DeleteNamedHeap( uint32 name, bool assertOnFail )
{
CNamedHeapInfo* pInfo = find_named_heap_info( name );
if ( pInfo )
{
Dbg_Message( "Deleting named heap %s", pInfo->mp_heap_name );
Dbg_Assert( pInfo->mp_region );
Dbg_Assert( pInfo->mp_heap );
RemoveHeap( pInfo->mp_heap );
delete pInfo->mp_region;
pInfo->mp_region = NULL;
pInfo->mp_heap = NULL;
pInfo->m_name = 0;
pInfo->m_used = false;
return true;
}
else
{
Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x to delete", name ) );
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void* Malloc( size_t size )
{
void *v = Mem::Manager::sHandle().New( size, true );
return v;
}
int Available()
{
return Mem::Manager::sHandle().Available();
}
void* ReallocateDown( size_t newSize, void *pOld )
{
return Mem::Manager::sHandle().ReallocateDown(newSize,pOld);
}
void* ReallocateUp( size_t newSize, void *pOld )
{
return Mem::Manager::sHandle().ReallocateUp(newSize,pOld);
}
void* ReallocateShrink( size_t newSize, void *pOld )
{
return Mem::Manager::sHandle().ReallocateShrink(newSize,pOld);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void Free( void* pAddr )
{
Mem::Manager::sHandle().Delete( pAddr );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool Valid( void* pAddr )
{
return Mem::Manager::sHandle().Valid( pAddr );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
size_t GetAllocSize( void* pAddr )
{
return Mem::Manager::sHandle().GetAllocSize( pAddr );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void* Realloc( void* mem, size_t newSize )
{
/* should really add resize functions to do this more efficiently */
void* ptr = NULL;
if ( newSize )
{
// Mem::Manager::sHandle().PushContext(Manager::sHandle().TopDownHeap());
ptr = Mem::Manager::sHandle().New( newSize, true );
// Mem::Manager::sHandle().PopContext();
}
if ( mem )
{
if ( ptr )
{
memmove ( ptr, mem, newSize );
}
Mem::Manager::sHandle().Delete( mem );
}
return ptr;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void* Calloc( size_t numObj, size_t sizeObj )
{
return Mem::Manager::sHandle().New(( numObj * sizeObj ), true );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// enable multiple threads to access the memory manager at once
void SetThreadSafe(bool safe)
{
#ifdef __PLAT_NGPS__
s_use_semaphore = safe;
#endif
}
bool IsThreadSafe( void )
{
#ifdef __PLAT_NGPS__
return s_use_semaphore;
#else
return false;
#endif
}
#ifdef __NOPT_ASSERT__
#define MAX_PROFILE_NAME 64
#define MAX_NUM_PROFILES 5000
#define MAX_LEVELS 32
class CMemProfile
{
public:
char m_type[MAX_PROFILE_NAME];
int m_blocks;
int m_size;
int m_depth;
int m_count; // number of pushes of same nam, same depth context
int m_link; // link forward to profile that has merged with this profile
};
//static CMemProfile s_profile_list[MAX_NUM_PROFILES]; // a flat array of all profiles
static CMemProfile * s_profile_list; // a dynamically allocated flat array of all profiles
static CMemProfile * s_profile_stack[MAX_LEVELS]; // stack of pushed profiles
static int s_profile_count = 0;
static CMemProfile* sp_current_profile = NULL; // current entry in profile list (will need to pop back....
static int s_profile_stack_depth = 0; // depth in stack (index into m_profile_stack[])
static int s_next_profile = 0; // index into array
//static int s_launched = false; // true after we've laucnhed the level once
static bool s_active = true;
void PushMemProfile(char *p_type)
{
if( s_active )
{
if (!s_profile_list)
{
if (Config::GotExtraMemory())
{
Mem::Manager::sHandle().PushContext(Manager::sHandle().DebugHeap());
s_profile_list = (CMemProfile *)Mem::Malloc(MAX_NUM_PROFILES *sizeof(CMemProfile));
Mem::Manager::sHandle().PopContext();
}
else
{
s_active = false; // no extra memory, or maybe it's an X-Box....
return;
}
}
if (s_next_profile >= MAX_NUM_PROFILES)
{
s_active = false;
return;
}
/*
if (!strcmp("LaunchLevel",p_type))
{
if (s_launched)
{
s_active = false;
return;
}
s_launched = true;
}
*/
// if we have a current profile, then push it
if (sp_current_profile)
{
Dbg_MsgAssert(s_profile_stack_depth < MAX_LEVELS,("mem profile stack overflow, unmatched push?"));
s_profile_stack[s_profile_stack_depth++] = sp_current_profile;
}
// get a new profile from the list
Dbg_MsgAssert(s_next_profile < MAX_NUM_PROFILES,("mem profile heap overflow, too many pushes"));
sp_current_profile = &s_profile_list[s_next_profile++];
// just copy over the memory containing the name
char *p = &(sp_current_profile->m_type[0]);
char *q = (char*) p_type;
for (int i=0;i<MAX_PROFILE_NAME;i++)
{
*p++ = *q++;
}
// and set the counters to zero
sp_current_profile->m_blocks = 0;
sp_current_profile->m_size = 0;
sp_current_profile->m_depth = s_profile_stack_depth;
sp_current_profile->m_count = 1;
sp_current_profile->m_link = 0;
// Then, search back through the list to see if there
// are any entries att he same level that have this same string
// if so, then zero the old instance, and add the size and count to this one
// there should only be one, as any other one would already be zeroed
int check = s_next_profile-2;
while (check>0
&& s_profile_list[check].m_depth == s_profile_stack_depth)
{
if (s_profile_list[check].m_depth == s_profile_stack_depth)
{
// same depth
if (strcmp(s_profile_list[check].m_type,p_type) == 0)
{
// same string, so bring this one forward to add to this
sp_current_profile->m_blocks += s_profile_list[check].m_blocks;
sp_current_profile->m_size += s_profile_list[check].m_size;
sp_current_profile->m_count += s_profile_list[check].m_count;
// and zero out the old one
s_profile_list[check].m_blocks = 0;
s_profile_list[check].m_size = 0;
// Patch in the index of the new profile,
// so late deletions can be accounted for
s_profile_list[check].m_link = s_profile_stack_depth - 1;
break;
}
}
check--;
}
}
}
/******************************************************************/
void PopMemProfile()
{
if( s_active)
{
Dbg_MsgAssert(sp_current_profile,("Popped one more memory profile than we pushed"));
// set time on the current profile
// sp_current_profile->m_end_time = Tmr::GetTimeInUSeconds();
if ( ! s_profile_stack_depth)
{
// nothing left on stack, so set current profile to NULL
sp_current_profile = NULL;
// update the count of valid profiles
s_profile_count = s_next_profile;
}
else
{
// get the last profile pushed on the stack
sp_current_profile = s_profile_stack[--s_profile_stack_depth];
}
}
}
// given an index in the the mem profile list
// then get the total size of all allocations
// at this level, or below
// up the next entry at the same depth, or the end of the list
int total_size(int n)
{
int size = s_profile_list[n].m_size;
int depth = s_profile_list[n].m_depth;
n++;
while (n < s_next_profile && s_profile_list[n].m_depth > depth)
{
size += s_profile_list[n].m_size;
n++;
}
return size;
}
#ifndef __PLAT_WN32__
// dump the memory profile in a tree format, like
//
// level stuff 100000
// peds 5000
// cars 23000
// other 72000
// skater stuff ......
void DumpMemProfile(int level, char *p_type)
{
char buf[512];
if( s_active )
{
printf ("\nDumping Memory Profile\n");
printf ("There are %d mem profile contexts\n",s_next_profile);
for (int i=0;i<s_next_profile;i++)
{
if (s_profile_list[i].m_depth <= level)
{
if (total_size(i))
{
sprintf (buf,"%2d: ",s_profile_list[i].m_depth);
dump_printf(buf);
for (int tab = 0;tab < s_profile_list[i].m_depth;tab++)
{
sprintf(buf," ");
dump_printf(buf);
}
sprintf (buf,">%10s____",Str::PrintThousands(total_size(i)));
dump_printf(buf);
if (s_profile_list[i].m_count == 1)
{
sprintf (buf,"%s\n",s_profile_list[i].m_type);
}
else
{
sprintf (buf,"%s (%d) (avg: %s)\n",s_profile_list[i].m_type,s_profile_list[i].m_count,Str::PrintThousands(total_size(i)/s_profile_list[i].m_count));
}
dump_printf(buf);
if (s_profile_list[i].m_depth < level && s_profile_list[i].m_size && total_size(i) != s_profile_list[i].m_size)
{
sprintf (buf,"%2d: ",s_profile_list[i].m_depth+1);
dump_printf(buf);
for (int tab2 = 0;tab2 < s_profile_list[i].m_depth+1;tab2++)
{
sprintf(buf," ");
dump_printf(buf);
}
sprintf (buf,">%10s____",Str::PrintThousands(s_profile_list[i].m_size));
dump_printf(buf);
sprintf (buf,"Untracked %s\n",s_profile_list[i].m_type);
dump_printf(buf);
}
}
}
}
}
else
{
printf ("Mem Profiler not active, probably overflowed, try restarting...\n");
}
}
#endif// __PLAT_WN32__
void AllocMemProfile(Allocator::BlockHeader* p_block)
{
if( s_active )
{
if (sp_current_profile)
{
sp_current_profile->m_blocks++;
// If it's on the debug heap, then set size to zero to avoid confusion
if (Mem::Manager::sHandle().GetContextAllocator() != Mem::Manager::sHandle().DebugHeap())
{
sp_current_profile->m_size += p_block->mSize;
}
p_block->mp_profile = sp_current_profile;
}
}
}
void FreeMemProfile(Allocator::BlockHeader* p_block)
{
if( s_active )
{
CMemProfile * p_profile = p_block->mp_profile;
if (p_profile)
{
// Dbg_MsgAssert(p_profile->m_blocks,("mutli-block freed out of context"));
// Skip over any that have been combined, until we find the final combined block
while (p_profile->m_link != 0)
{
p_profile = &s_profile_list[p_profile->m_link];
}
p_profile->m_blocks--;
p_profile->m_size -= p_block->mSize;
p_profile = NULL;
}
}
}
#else
void PushMemProfile(char *p_type)
{
}
void PopMemProfile()
{
}
#ifndef __PLAT_WN32__
void DumpMemProfile(int level, char *p_type)
{
}
#endif// __PLAT_WN32__
void AllocMemProfile(Allocator::BlockHeader* p_block)
{
}
void FreeMemProfile(Allocator::BlockHeader* p_block)
{
}
#endif __NOPT_ASSERT__
} // namespace Mem