csgo-2018-source/engine/zone.cpp
2021-07-24 21:11:47 -07:00

278 lines
7.3 KiB
C++

//===== 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<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 );
}