278 lines
7.3 KiB
C++
278 lines
7.3 KiB
C++
|
//===== Copyright <20> 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<int, int> 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 );
|
|||
|
}
|