|
|
//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============// |
|
|
// |
|
|
// Purpose: |
|
|
// |
|
|
//=============================================================================// |
|
|
|
|
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
|
#define WIN_32_LEAN_AND_MEAN |
|
|
#include <windows.h> |
|
|
#define VA_COMMIT_FLAGS MEM_COMMIT |
|
|
#define VA_RESERVE_FLAGS MEM_RESERVE |
|
|
#elif defined( _X360 ) |
|
|
#define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES) |
|
|
#define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES) |
|
|
#elif defined( _PS3 ) |
|
|
#include "sys/memory.h" |
|
|
#include "sys/mempool.h" |
|
|
#include "sys/process.h" |
|
|
#include <sys/vm.h> |
|
|
#endif |
|
|
|
|
|
#include "tier0/dbg.h" |
|
|
#include "memstack.h" |
|
|
#include "utlmap.h" |
|
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
#ifdef _WIN32 |
|
|
#pragma warning(disable:4073) |
|
|
#pragma init_seg(lib) |
|
|
#endif |
|
|
|
|
|
static volatile bool bSpewAllocations = false; // TODO: Register CMemoryStacks with g_pMemAlloc, so it can spew a summary |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
MEMALLOC_DEFINE_EXTERNAL_TRACKING(CMemoryStack); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
CMemoryStack::CMemoryStack() |
|
|
: m_pBase( NULL ), |
|
|
m_pNextAlloc( NULL ), |
|
|
m_pAllocLimit( NULL ), |
|
|
m_pCommitLimit( NULL ), |
|
|
m_alignment( 16 ), |
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
m_commitSize( 0 ), |
|
|
m_minCommit( 0 ), |
|
|
#ifdef _PS3 |
|
|
m_pVirtualMemorySection( NULL ), |
|
|
#endif |
|
|
#endif |
|
|
m_maxSize( 0 ), |
|
|
m_bRegisteredAllocation( false ) |
|
|
{ |
|
|
m_pszAllocOwner = strdup( "CMemoryStack unattributed" ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
CMemoryStack::~CMemoryStack() |
|
|
{ |
|
|
if ( m_pBase ) |
|
|
Term(); |
|
|
free( m_pszAllocOwner ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
bool CMemoryStack::Init( const char *pszAllocOwner, unsigned maxSize, unsigned commitSize, unsigned initialCommit, unsigned alignment ) |
|
|
{ |
|
|
Assert( !m_pBase ); |
|
|
|
|
|
m_bPhysical = false; |
|
|
|
|
|
m_maxSize = maxSize; |
|
|
m_alignment = AlignValue( alignment, 4 ); |
|
|
|
|
|
Assert( m_alignment == alignment ); |
|
|
Assert( m_maxSize > 0 ); |
|
|
|
|
|
SetAllocOwner( pszAllocOwner ); |
|
|
|
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
|
|
|
#ifdef _PS3 |
|
|
// Memory can only be committed in page-size increments on PS3 |
|
|
static const unsigned PS3_PAGE_SIZE = 64*1024; |
|
|
if ( commitSize < PS3_PAGE_SIZE ) |
|
|
commitSize = PS3_PAGE_SIZE; |
|
|
#endif |
|
|
|
|
|
if ( commitSize != 0 ) |
|
|
{ |
|
|
m_commitSize = commitSize; |
|
|
} |
|
|
|
|
|
unsigned pageSize; |
|
|
|
|
|
#ifdef _PS3 |
|
|
pageSize = PS3_PAGE_SIZE; |
|
|
#elif defined( _X360 ) |
|
|
pageSize = 64 * 1024; |
|
|
#else |
|
|
SYSTEM_INFO sysInfo; |
|
|
GetSystemInfo( &sysInfo ); |
|
|
Assert( !( sysInfo.dwPageSize & (sysInfo.dwPageSize-1)) ); |
|
|
pageSize = sysInfo.dwPageSize; |
|
|
#endif |
|
|
|
|
|
if ( m_commitSize == 0 ) |
|
|
{ |
|
|
m_commitSize = pageSize; |
|
|
} |
|
|
else |
|
|
{ |
|
|
m_commitSize = AlignValue( m_commitSize, pageSize ); |
|
|
} |
|
|
|
|
|
m_maxSize = AlignValue( m_maxSize, m_commitSize ); |
|
|
|
|
|
Assert( m_maxSize % pageSize == 0 && m_commitSize % pageSize == 0 && m_commitSize <= m_maxSize ); |
|
|
|
|
|
#ifdef _WIN32 |
|
|
m_pBase = (unsigned char *)VirtualAlloc( NULL, m_maxSize, VA_RESERVE_FLAGS, PAGE_NOACCESS ); |
|
|
#else |
|
|
m_pVirtualMemorySection = g_pMemAlloc->AllocateVirtualMemorySection( m_maxSize ); |
|
|
if ( !m_pVirtualMemorySection ) |
|
|
{ |
|
|
Warning( "AllocateVirtualMemorySection failed( size=%d )\n", m_maxSize ); |
|
|
Assert( 0 ); |
|
|
m_pBase = NULL; |
|
|
} |
|
|
else |
|
|
{ |
|
|
m_pBase = ( byte* ) m_pVirtualMemorySection->GetBaseAddress(); |
|
|
} |
|
|
#endif |
|
|
if ( !m_pBase ) |
|
|
{ |
|
|
#if !defined( NO_MALLOC_OVERRIDE ) |
|
|
g_pMemAlloc->OutOfMemory(); |
|
|
#endif |
|
|
return false; |
|
|
} |
|
|
m_pCommitLimit = m_pNextAlloc = m_pBase; |
|
|
|
|
|
if ( initialCommit ) |
|
|
{ |
|
|
initialCommit = AlignValue( initialCommit, m_commitSize ); |
|
|
Assert( initialCommit <= m_maxSize ); |
|
|
bool bInitialCommitSucceeded = false; |
|
|
#ifdef _WIN32 |
|
|
bInitialCommitSucceeded = !!VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE ); |
|
|
#else |
|
|
m_pVirtualMemorySection->CommitPages( m_pCommitLimit, initialCommit ); |
|
|
bInitialCommitSucceeded = true; |
|
|
#endif |
|
|
if ( !bInitialCommitSucceeded ) |
|
|
{ |
|
|
#if !defined( NO_MALLOC_OVERRIDE ) |
|
|
g_pMemAlloc->OutOfMemory( initialCommit ); |
|
|
#endif |
|
|
return false; |
|
|
} |
|
|
m_minCommit = initialCommit; |
|
|
m_pCommitLimit += initialCommit; |
|
|
RegisterAllocation(); |
|
|
} |
|
|
|
|
|
#else |
|
|
m_pBase = (byte*)MemAlloc_AllocAligned( m_maxSize, alignment ? alignment : 1 ); |
|
|
m_pNextAlloc = m_pBase; |
|
|
m_pCommitLimit = m_pBase + m_maxSize; |
|
|
#endif |
|
|
|
|
|
m_pAllocLimit = m_pBase + m_maxSize; |
|
|
|
|
|
return ( m_pBase != NULL ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
#ifdef _GAMECONSOLE |
|
|
bool CMemoryStack::InitPhysical( const char *pszAllocOwner, uint size, uint nBaseAddrAlignment, uint alignment, uint32 nFlags ) |
|
|
{ |
|
|
m_bPhysical = true; |
|
|
|
|
|
m_maxSize = m_commitSize = size; |
|
|
m_alignment = AlignValue( alignment, 4 ); |
|
|
|
|
|
SetAllocOwner( pszAllocOwner ); |
|
|
|
|
|
#ifdef _X360 |
|
|
int flags = PAGE_READWRITE | nFlags; |
|
|
if ( size >= 16*1024*1024 ) |
|
|
{ |
|
|
flags |= MEM_16MB_PAGES; |
|
|
} |
|
|
else |
|
|
{ |
|
|
flags |= MEM_LARGE_PAGES; |
|
|
} |
|
|
m_pBase = (unsigned char *)XPhysicalAlloc( m_maxSize, MAXULONG_PTR, nBaseAddrAlignment, flags ); |
|
|
#elif defined (_PS3) |
|
|
m_pBase = (byte*)nFlags; |
|
|
m_pBase = (byte*)AlignValue( (uintp)m_pBase, m_alignment ); |
|
|
#else |
|
|
#pragma error |
|
|
#endif |
|
|
|
|
|
Assert( m_pBase ); |
|
|
m_pNextAlloc = m_pBase; |
|
|
m_pCommitLimit = m_pBase + m_maxSize; |
|
|
m_pAllocLimit = m_pBase + m_maxSize; |
|
|
|
|
|
RegisterAllocation(); |
|
|
return ( m_pBase != NULL ); |
|
|
} |
|
|
#endif |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CMemoryStack::Term() |
|
|
{ |
|
|
FreeAll(); |
|
|
if ( m_pBase ) |
|
|
{ |
|
|
#ifdef _GAMECONSOLE |
|
|
if ( m_bPhysical ) |
|
|
{ |
|
|
#if defined( _X360 ) |
|
|
XPhysicalFree( m_pBase ); |
|
|
#elif defined( _PS3 ) |
|
|
#else |
|
|
#pragma error |
|
|
#endif |
|
|
m_pCommitLimit = m_pBase = NULL; |
|
|
m_maxSize = 0; |
|
|
RegisterDeallocation(true); |
|
|
m_bPhysical = false; |
|
|
return; |
|
|
} |
|
|
#endif // _GAMECONSOLE |
|
|
|
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
#if defined(_WIN32) |
|
|
VirtualFree( m_pBase, 0, MEM_RELEASE ); |
|
|
#else |
|
|
m_pVirtualMemorySection->Release(); |
|
|
m_pVirtualMemorySection = NULL; |
|
|
#endif |
|
|
#else |
|
|
MemAlloc_FreeAligned( m_pBase ); |
|
|
#endif |
|
|
m_pCommitLimit = m_pBase = NULL; |
|
|
m_maxSize = 0; |
|
|
RegisterDeallocation(true); |
|
|
} |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
int CMemoryStack::GetSize() |
|
|
{ |
|
|
if ( m_bPhysical ) |
|
|
return m_maxSize; |
|
|
|
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
return m_pCommitLimit - m_pBase; |
|
|
#else |
|
|
return m_maxSize; |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
bool CMemoryStack::CommitTo( byte *pNextAlloc ) RESTRICT |
|
|
{ |
|
|
if ( m_bPhysical ) |
|
|
{ |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
unsigned char * pNewCommitLimit = AlignValue( pNextAlloc, m_commitSize ); |
|
|
ptrdiff_t commitSize = pNewCommitLimit - m_pCommitLimit; |
|
|
|
|
|
if( m_pCommitLimit + commitSize > m_pAllocLimit ) |
|
|
{ |
|
|
return false; |
|
|
} |
|
|
|
|
|
if ( pNewCommitLimit > m_pCommitLimit ) |
|
|
{ |
|
|
RegisterDeallocation(false); |
|
|
bool bAllocationSucceeded = false; |
|
|
#ifdef _WIN32 |
|
|
bAllocationSucceeded = !!VirtualAlloc( m_pCommitLimit, commitSize, VA_COMMIT_FLAGS, PAGE_READWRITE ); |
|
|
#else |
|
|
bAllocationSucceeded = m_pVirtualMemorySection->CommitPages( m_pCommitLimit, commitSize ); |
|
|
#endif |
|
|
if ( !bAllocationSucceeded ) |
|
|
{ |
|
|
#if !defined( NO_MALLOC_OVERRIDE ) |
|
|
g_pMemAlloc->OutOfMemory( commitSize ); |
|
|
#endif |
|
|
return false; |
|
|
} |
|
|
m_pCommitLimit = pNewCommitLimit; |
|
|
RegisterAllocation(); |
|
|
} |
|
|
else if ( pNewCommitLimit < m_pCommitLimit ) |
|
|
{ |
|
|
if ( m_pNextAlloc > pNewCommitLimit ) |
|
|
{ |
|
|
Warning( "ATTEMPTED TO DECOMMIT OWNED MEMORY STACK SPACE\n" ); |
|
|
pNewCommitLimit = AlignValue( m_pNextAlloc, m_commitSize ); |
|
|
} |
|
|
|
|
|
if ( pNewCommitLimit < m_pCommitLimit ) |
|
|
{ |
|
|
RegisterDeallocation(false); |
|
|
ptrdiff_t decommitSize = m_pCommitLimit - pNewCommitLimit; |
|
|
#ifdef _WIN32 |
|
|
VirtualFree( pNewCommitLimit, decommitSize, MEM_DECOMMIT ); |
|
|
#else |
|
|
m_pVirtualMemorySection->DecommitPages( pNewCommitLimit, decommitSize ); |
|
|
#endif |
|
|
m_pCommitLimit = pNewCommitLimit; |
|
|
RegisterAllocation(); |
|
|
} |
|
|
} |
|
|
|
|
|
return true; |
|
|
#else |
|
|
return false; |
|
|
#endif |
|
|
} |
|
|
|
|
|
// Identify the owner of this memory stack's memory |
|
|
void CMemoryStack::SetAllocOwner( const char *pszAllocOwner ) |
|
|
{ |
|
|
if ( !pszAllocOwner || !V_strcmp( m_pszAllocOwner, pszAllocOwner ) ) |
|
|
return; |
|
|
free( m_pszAllocOwner ); |
|
|
m_pszAllocOwner = strdup( pszAllocOwner ); |
|
|
} |
|
|
|
|
|
void CMemoryStack::RegisterAllocation() |
|
|
{ |
|
|
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here: |
|
|
if ( IsPS3() && m_bPhysical ) |
|
|
return; |
|
|
|
|
|
if ( GetSize() ) |
|
|
{ |
|
|
if ( m_bRegisteredAllocation ) |
|
|
Warning( "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" ); |
|
|
|
|
|
// NOTE: we deliberately don't use MemAlloc_RegisterExternalAllocation. CMemoryStack needs to bypass 'GetActualDbgInfo' |
|
|
// due to the way it allocates memory: there's just one representative memory address (m_pBase), it grows at unpredictable |
|
|
// times (in CommitTo, not every Alloc call) and it is freed en-masse (instead of freeing each individual allocation). |
|
|
MemAlloc_RegisterAllocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 ); |
|
|
} |
|
|
m_bRegisteredAllocation = true; |
|
|
|
|
|
// Temp memorystack spew: very useful when we crash out of memory |
|
|
if ( IsGameConsole() && bSpewAllocations ) Msg( "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner ); |
|
|
} |
|
|
|
|
|
void CMemoryStack::RegisterDeallocation( bool bShouldSpewSize ) |
|
|
{ |
|
|
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here: |
|
|
if ( IsPS3() && m_bPhysical ) |
|
|
return; |
|
|
|
|
|
if ( GetSize() ) |
|
|
{ |
|
|
if ( !m_bRegisteredAllocation ) |
|
|
Warning( "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" ); |
|
|
MemAlloc_RegisterDeallocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 ); |
|
|
} |
|
|
m_bRegisteredAllocation = false; |
|
|
|
|
|
// Temp memorystack spew: very useful when we crash out of memory |
|
|
if ( bShouldSpewSize && IsGameConsole() && bSpewAllocations ) Msg( "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner ); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit ) |
|
|
{ |
|
|
mark = AlignValue( mark, m_alignment ); |
|
|
byte *pAllocPoint = m_pBase + mark; |
|
|
|
|
|
Assert( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc ); |
|
|
if ( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc ) |
|
|
{ |
|
|
m_pNextAlloc = pAllocPoint; |
|
|
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE |
|
|
if ( bDecommit && !m_bPhysical ) |
|
|
{ |
|
|
CommitTo( MAX( m_pNextAlloc, (m_pBase + m_minCommit) ) ); |
|
|
} |
|
|
#endif |
|
|
} |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CMemoryStack::FreeAll( bool bDecommit ) |
|
|
{ |
|
|
if ( m_pBase && ( m_pBase < m_pCommitLimit ) ) |
|
|
{ |
|
|
FreeToAllocPoint( 0, bDecommit ); |
|
|
} |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CMemoryStack::Access( void **ppRegion, unsigned *pBytes ) |
|
|
{ |
|
|
*ppRegion = m_pBase; |
|
|
*pBytes = ( m_pNextAlloc - m_pBase); |
|
|
} |
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CMemoryStack::PrintContents() |
|
|
{ |
|
|
Msg( "Total used memory: %d\n", GetUsed() ); |
|
|
Msg( "Total committed memory: %d\n", GetSize() ); |
|
|
} |
|
|
|
|
|
#ifdef _X360 |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// |
|
|
// A memory stack used for allocating physical memory on the 360 (can't commit/decommit) |
|
|
// |
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
MEMALLOC_DEFINE_EXTERNAL_TRACKING(CPhysicalMemoryStack); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Constructor, destructor |
|
|
//----------------------------------------------------------------------------- |
|
|
CPhysicalMemoryStack::CPhysicalMemoryStack() : |
|
|
m_nAlignment( 16 ), m_nAdditionalFlags( 0 ), m_nUsage( 0 ), m_nPeakUsage( 0 ), m_pLastAllocedChunk( NULL ), |
|
|
m_nFirstAvailableChunk( 0 ), m_nChunkSizeInBytes( 0 ), m_ExtraChunks( 32, 32 ), m_nFramePeakUsage( 0 ) |
|
|
{ |
|
|
m_InitialChunk.m_pBase = NULL; |
|
|
m_InitialChunk.m_pNextAlloc = NULL; |
|
|
m_InitialChunk.m_pAllocLimit = NULL; |
|
|
} |
|
|
|
|
|
CPhysicalMemoryStack::~CPhysicalMemoryStack() |
|
|
{ |
|
|
Term(); |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Init, shutdown |
|
|
//----------------------------------------------------------------------------- |
|
|
bool CPhysicalMemoryStack::Init( size_t nChunkSizeInBytes, size_t nAlignment, int nInitialChunkCount, uint32 nAdditionalFlags ) |
|
|
{ |
|
|
Assert( !m_InitialChunk.m_pBase ); |
|
|
|
|
|
m_pLastAllocedChunk = NULL; |
|
|
m_nAdditionalFlags = nAdditionalFlags; |
|
|
m_nFirstAvailableChunk = 0; |
|
|
m_nUsage = 0; |
|
|
m_nFramePeakUsage = 0; |
|
|
m_nPeakUsage = 0; |
|
|
m_nAlignment = AlignValue( nAlignment, 4 ); |
|
|
|
|
|
// Chunk size must be aligned to the 360 page size |
|
|
size_t nInitMemorySize = nChunkSizeInBytes * nInitialChunkCount; |
|
|
nChunkSizeInBytes = AlignValue( nChunkSizeInBytes, 64 * 1024 ); |
|
|
m_nChunkSizeInBytes = nChunkSizeInBytes; |
|
|
|
|
|
// Fix up initial chunk count to get at least as much memory as requested |
|
|
// based on changes to the chunk size owing to page alignment issues |
|
|
nInitialChunkCount = ( nInitMemorySize + nChunkSizeInBytes - 1 ) / nChunkSizeInBytes; |
|
|
|
|
|
int nFlags = PAGE_READWRITE | nAdditionalFlags; |
|
|
int nAllocationSize = m_nChunkSizeInBytes * nInitialChunkCount; |
|
|
if ( nAllocationSize >= 16*1024*1024 ) |
|
|
{ |
|
|
nFlags |= MEM_16MB_PAGES; |
|
|
} |
|
|
else |
|
|
{ |
|
|
nFlags |= MEM_LARGE_PAGES; |
|
|
} |
|
|
m_InitialChunk.m_pBase = (uint8*)XPhysicalAlloc( nAllocationSize, MAXULONG_PTR, 0, nFlags ); |
|
|
if ( !m_InitialChunk.m_pBase ) |
|
|
{ |
|
|
m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pAllocLimit = NULL; |
|
|
g_pMemAlloc->OutOfMemory(); |
|
|
return false; |
|
|
} |
|
|
|
|
|
m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pBase; |
|
|
m_InitialChunk.m_pAllocLimit = m_InitialChunk.m_pBase + nAllocationSize; |
|
|
|
|
|
MemAlloc_RegisterExternalAllocation( CPhysicalMemoryStack, m_InitialChunk.m_pBase, XPhysicalSize( m_InitialChunk.m_pBase ) ); |
|
|
return true; |
|
|
} |
|
|
|
|
|
void CPhysicalMemoryStack::Term() |
|
|
{ |
|
|
FreeAll(); |
|
|
if ( m_InitialChunk.m_pBase ) |
|
|
{ |
|
|
MemAlloc_RegisterExternalDeallocation( CPhysicalMemoryStack, m_InitialChunk.m_pBase, XPhysicalSize( m_InitialChunk.m_pBase ) ); |
|
|
XPhysicalFree( m_InitialChunk.m_pBase ); |
|
|
m_InitialChunk.m_pBase = m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pAllocLimit = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Returns the total allocation size |
|
|
//----------------------------------------------------------------------------- |
|
|
size_t CPhysicalMemoryStack::GetSize() const |
|
|
{ |
|
|
size_t nBaseSize = (intp)m_InitialChunk.m_pAllocLimit - (intp)m_InitialChunk.m_pBase; |
|
|
return nBaseSize + m_nChunkSizeInBytes * m_ExtraChunks.Count(); |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Allocate from the 'overflow' buffers, only happens if the initial allocation |
|
|
// isn't good enough |
|
|
//----------------------------------------------------------------------------- |
|
|
void *CPhysicalMemoryStack::AllocFromOverflow( size_t nSizeInBytes ) |
|
|
{ |
|
|
// Completely full chunks are moved to the front and skipped |
|
|
int nCount = m_ExtraChunks.Count(); |
|
|
for ( int i = m_nFirstAvailableChunk; i < nCount; ++i ) |
|
|
{ |
|
|
PhysicalChunk_t &chunk = m_ExtraChunks[i]; |
|
|
|
|
|
// Here we can check if a chunk is full and move it to the head |
|
|
// of the list. We can't do it immediately *after* allocation |
|
|
// because something may later free up some of the memory |
|
|
if ( chunk.m_pNextAlloc == chunk.m_pAllocLimit ) |
|
|
{ |
|
|
if ( i > 0 ) |
|
|
{ |
|
|
m_ExtraChunks.FastRemove( i ); |
|
|
m_ExtraChunks.InsertBefore( 0 ); |
|
|
} |
|
|
++m_nFirstAvailableChunk; |
|
|
continue; |
|
|
} |
|
|
|
|
|
void *pResult = chunk.m_pNextAlloc; |
|
|
uint8 *pNextAlloc = chunk.m_pNextAlloc + nSizeInBytes; |
|
|
if ( pNextAlloc > chunk.m_pAllocLimit ) |
|
|
continue; |
|
|
|
|
|
chunk.m_pNextAlloc = pNextAlloc; |
|
|
m_pLastAllocedChunk = &chunk; |
|
|
return pResult; |
|
|
} |
|
|
|
|
|
// No extra chunks to use; add a new one |
|
|
int i = m_ExtraChunks.AddToTail(); |
|
|
PhysicalChunk_t &chunk = m_ExtraChunks[i]; |
|
|
|
|
|
int nFlags = PAGE_READWRITE | MEM_LARGE_PAGES | m_nAdditionalFlags; |
|
|
chunk.m_pBase = (uint8*)XPhysicalAlloc( m_nChunkSizeInBytes, MAXULONG_PTR, 0, nFlags ); |
|
|
if ( !chunk.m_pBase ) |
|
|
{ |
|
|
chunk.m_pNextAlloc = chunk.m_pAllocLimit = NULL; |
|
|
m_pLastAllocedChunk = NULL; |
|
|
g_pMemAlloc->OutOfMemory(); |
|
|
return NULL; |
|
|
} |
|
|
MemAlloc_RegisterExternalAllocation( CPhysicalMemoryStack, chunk.m_pBase, XPhysicalSize( chunk.m_pBase ) ); |
|
|
|
|
|
m_pLastAllocedChunk = &chunk; |
|
|
chunk.m_pNextAlloc = chunk.m_pBase + nSizeInBytes; |
|
|
chunk.m_pAllocLimit = chunk.m_pBase + m_nChunkSizeInBytes; |
|
|
return chunk.m_pBase; |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Allows us to free a portion of the previous allocation |
|
|
//----------------------------------------------------------------------------- |
|
|
void CPhysicalMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bUnused ) |
|
|
{ |
|
|
mark = AlignValue( mark, m_nAlignment ); |
|
|
uint8 *pAllocPoint = m_pLastAllocedChunk->m_pBase + mark; |
|
|
Assert( pAllocPoint >= m_pLastAllocedChunk->m_pBase && pAllocPoint <= m_pLastAllocedChunk->m_pNextAlloc ); |
|
|
if ( pAllocPoint >= m_pLastAllocedChunk->m_pBase && pAllocPoint <= m_pLastAllocedChunk->m_pNextAlloc ) |
|
|
{ |
|
|
m_nUsage -= (intp)m_pLastAllocedChunk->m_pNextAlloc - (intp)pAllocPoint; |
|
|
m_pLastAllocedChunk->m_pNextAlloc = pAllocPoint; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
// Free overflow buffers, mark initial buffer as empty |
|
|
//----------------------------------------------------------------------------- |
|
|
void CPhysicalMemoryStack::FreeAll( bool bUnused ) |
|
|
{ |
|
|
m_nUsage = 0; |
|
|
m_nFramePeakUsage = 0; |
|
|
m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pBase; |
|
|
m_pLastAllocedChunk = NULL; |
|
|
m_nFirstAvailableChunk = 0; |
|
|
int nCount = m_ExtraChunks.Count(); |
|
|
for ( int i = 0; i < nCount; ++i ) |
|
|
{ |
|
|
PhysicalChunk_t &chunk = m_ExtraChunks[i]; |
|
|
MemAlloc_RegisterExternalDeallocation( CPhysicalMemoryStack, chunk.m_pBase, XPhysicalSize( chunk.m_pBase ) ); |
|
|
XPhysicalFree( chunk.m_pBase ); |
|
|
} |
|
|
m_ExtraChunks.RemoveAll(); |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------- |
|
|
|
|
|
void CPhysicalMemoryStack::PrintContents() |
|
|
{ |
|
|
Msg( "Total used memory: %8d\n", GetUsed() ); |
|
|
Msg( "Peak used memory: %8d\n", GetPeakUsed() ); |
|
|
Msg( "Total allocated memory: %8d\n", GetSize() ); |
|
|
} |
|
|
|
|
|
|
|
|
#endif // _X360
|
|
|
|