diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/tier1/memstack.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/tier1/memstack.cpp')
| -rw-r--r-- | external/vpc/tier1/memstack.cpp | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/external/vpc/tier1/memstack.cpp b/external/vpc/tier1/memstack.cpp new file mode 100644 index 0000000..9ab0b6f --- /dev/null +++ b/external/vpc/tier1/memstack.cpp @@ -0,0 +1,641 @@ +//========= Copyright � 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 |