//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // ZONE MEMORY ALLOCATION // // There is never any space between memblocks, and there will never be two // contiguous free memblocks. // // The rover can be left pointing at a non-empty block // // The zone calls are pretty much only used for small strings and structures, // all big things are allocated on the hunk. //===========================================================================// #include "basetypes.h" #include "zone.h" #include "host.h" #include "tier1/strtools.h" #include "tier1/utldict.h" #include "tier0/icommandline.h" #include "memstack.h" #include "datacache/idatacache.h" #include "sys_dll.h" #include "tier3/tier3.h" #include "tier0/memalloc.h" // NOTE: This has to be the last file included! #include "tier0/memdbgon.h" #define KB (1024) #define MB (1024*1024) #define MINIMUM_WIN_MEMORY (48*MB) // FIXME: copy from sys_dll.cpp, find a common header at some point // PORTAL 2 SHIPPING CHANGE // We're sacrificing a little perf (~1% in pathological case) for 12 MB of memory back on X360 // #ifdef _X360 // #define HUNK_USE_16MB_PAGE // #endif CMemoryStack g_HunkMemoryStack; #ifdef HUNK_USE_16MB_PAGE CMemoryStack g_HunkOverflow; static bool g_bWarnedOverflow; #define SIZE_PHYSICAL_HUNK (16*MB) #endif const int HUNK_COMMIT_FLOOR = ( IsGameConsole() ? 4/*18*/ : 40 )*MB; const char *CHunkAllocCredit::s_DbgInfoStack[ DBG_INFO_STACK_DEPTH ]; int CHunkAllocCredit::s_DbgInfoStackDepth = -1; #if !defined( _CERT ) ConVar hunk_track_allocation_types( "hunk_track_allocation_types", "1", FCVAR_CHEAT ); #else ConVar hunk_track_allocation_types( "hunk_track_allocation_types", "0", FCVAR_CHEAT ); #endif CUtlDict g_HunkAllocationsByName; struct hunkalloc_t { int index, size; }; int HunkAllocSortFunc( const void *a, const void *b ) { const hunkalloc_t *A = (const hunkalloc_t *)a, *B = (const hunkalloc_t *)b; return ( A->size > B->size ) ? -1 : +1; } CON_COMMAND_F( hunk_print_allocations, "", FCVAR_CLIENTCMD_CAN_EXECUTE ) { Msg( "Hunk allocations:\n"); hunkalloc_t *items = new hunkalloc_t[ g_HunkAllocationsByName.Count() ]; int numItems = 0, total = 0; for ( int i = g_HunkAllocationsByName.First(); i != g_HunkAllocationsByName.InvalidIndex(); i = g_HunkAllocationsByName.Next( i ) ) { if ( !g_HunkAllocationsByName.Element( i ) ) continue; hunkalloc_t item = { i, g_HunkAllocationsByName.Element( i ) }; items[numItems++] = item; total += item.size; } qsort( items, numItems, sizeof( hunkalloc_t ), HunkAllocSortFunc ); Msg( " %55s:%10d\n", "TOTAL:", total ); for ( int i = 0; i < numItems; i++ ) { Msg( " %55s:%10d\n", g_HunkAllocationsByName.GetElementName( items[i].index ), items[i].size ); } delete [] items; #if defined( _X360 ) xBudgetInfo_t budgetInfo; budgetInfo.BSPSize = total; XBX_rBudgetInfo( &budgetInfo ); #endif } static int GetTargetCacheSize() { int nMemLimit = host_parms.memsize - Hunk_Size(); if ( nMemLimit < 0x100000 ) { nMemLimit = 0x100000; } return nMemLimit; } /* =================== Hunk_AllocName =================== */ void *Hunk_AllocName(int size, const char *name, bool bClear) { if ( hunk_track_allocation_types.GetBool() ) { MEM_ALLOC_CREDIT(); if ( !name ) { name = "unknown"; } int i = g_HunkAllocationsByName.Find( name ); if ( i == g_HunkAllocationsByName.InvalidIndex() ) { i = g_HunkAllocationsByName.Insert( name ); g_HunkAllocationsByName[i] = size; } else { g_HunkAllocationsByName[i] += size; } } void *p = g_HunkMemoryStack.Alloc( size, bClear ); #ifdef _GAMECONSOLE int overflowAmt = g_HunkMemoryStack.GetCurrentAllocPoint() - HUNK_COMMIT_FLOOR; if ( ( overflowAmt > 0 ) && ( overflowAmt <= size ) ) Warning( "HUNK OVERFLOW! Map BSP data consuming %d bytes more memory than expected...\n", overflowAmt ); #endif if ( p ) return p; #ifdef HUNK_USE_16MB_PAGE if ( !g_bWarnedOverflow ) { g_bWarnedOverflow = true; DevMsg( "Note: Hunk base page exhausted\n" ); } p = g_HunkOverflow.Alloc( size, bClear ); if ( p ) return p; #endif Error( "Engine hunk overflow!\n" ); return NULL; } /* =================== Hunk_Alloc =================== */ int Hunk_LowMark(void) { return (int)( g_HunkMemoryStack.GetCurrentAllocPoint() ); } void Hunk_FreeToLowMark(int mark) { Assert( mark < g_HunkMemoryStack.GetSize() ); #ifdef HUNK_USE_16MB_PAGE g_HunkOverflow.FreeAll( false ); g_bWarnedOverflow = false; #endif g_HunkMemoryStack.FreeToAllocPoint( mark, false ); g_HunkAllocationsByName.RemoveAll(); } int Hunk_MallocSize() { #ifdef HUNK_USE_16MB_PAGE return g_HunkMemoryStack.GetSize() + g_HunkOverflow.GetSize(); #else return g_HunkMemoryStack.GetSize(); #endif } int Hunk_Size() { #ifdef HUNK_USE_16MB_PAGE return g_HunkMemoryStack.GetUsed() + g_HunkOverflow.GetUsed(); #else return g_HunkMemoryStack.GetUsed(); #endif } void Hunk_Print() { #ifdef HUNK_USE_16MB_PAGE Msg( "Total used memory: %d (%d/%d)\n", Hunk_Size(), g_HunkMemoryStack.GetUsed(), g_HunkOverflow.GetUsed() ); Msg( "Total committed memory: %d (%d/%d)\n", Hunk_MallocSize(), g_HunkMemoryStack.GetSize(), g_HunkOverflow.GetSize() ); #else Msg( "Total used memory: %d\n", Hunk_Size() ); Msg( "Total committed memory: %d\n", Hunk_MallocSize() ); #endif } void Hunk_OnMapStart( int nEstimatedBytes ) { int nToCommit = MAX( nEstimatedBytes, HUNK_COMMIT_FLOOR ); #ifndef HUNK_USE_16MB_PAGE CMemoryStack *pStack = &g_HunkMemoryStack; #else CMemoryStack *pStack = &g_HunkOverflow; nToCommit -= SIZE_PHYSICAL_HUNK; #endif if ( developer.GetBool() ) { DevMsg( "Hunk_OnMapStart: %d\n", nToCommit ); } if ( nToCommit > 0 ) { pStack->CommitSize( nToCommit ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Memory_Init( void ) { MEM_ALLOC_CREDIT(); #ifdef PLATFORM_64BITS // Seems to need to be larger to not get exhausted on // 64-bit. Perhaps because of larger pointer sizes. int nMaxBytes = 128*MB; #else int nMaxBytes = 64*MB; #endif const int commitIncrement = 64*KB; #ifndef HUNK_USE_16MB_PAGE const int nInitialCommit = MIN( HUNK_COMMIT_FLOOR, nMaxBytes ); while ( !g_HunkMemoryStack.Init( "g_HunkMemoryStack", nMaxBytes, commitIncrement, nInitialCommit ) ) { Warning( "Unable to allocate %d MB of memory, trying %d MB instead\n", nMaxBytes, nMaxBytes/2 ); nMaxBytes /= 2; if ( nMaxBytes < MINIMUM_WIN_MEMORY ) { Error( "Failed to allocate minimum memory requirement for game (%d MB)\n", MINIMUM_WIN_MEMORY/MB); } } #else if ( !g_HunkMemoryStack.InitPhysical( "g_HunkMemoryStack", SIZE_PHYSICAL_HUNK, 4096 ) || !g_HunkOverflow.Init( "g_HunkOverflow", nMaxBytes - SIZE_PHYSICAL_HUNK, commitIncrement, (SIZE_PHYSICAL_HUNK < HUNK_COMMIT_FLOOR ) ? HUNK_COMMIT_FLOOR - SIZE_PHYSICAL_HUNK : 0 ) ) { Error( "Failed to allocate minimum memory requirement for game (%d MB)\n", nMaxBytes ); } #endif g_pDataCache->SetSize( GetTargetCacheSize() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Memory_Shutdown( void ) { g_HunkMemoryStack.FreeAll(); // This disconnects the engine data cache g_pDataCache->SetSize( 0 ); }