jedi-academy/codemp/qcommon/z_memman_console.cpp
2013-04-23 15:21:39 +10:00

1825 lines
39 KiB
C++

/*
* UNPUBLISHED -- Rights reserved under the copyright laws of the
* United States. Use of a copyright notice is precautionary only and
* does not imply publication or disclosure.
*
* THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION
* OF VICARIOUS VISIONS, INC. ANY DUPLICATION, MODIFICATION,
* DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR
* EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC.
*/
/*
* ZONE MEMORY MANAGER
*
* Goals:
* 1. Minimize overhead
* 2. Minimize fragmentation
*
* Constraints:
* 1. Maximum allocated block size is 32MB
* 2. Maximum 64 different memory tags supported
* 3. Maximum 256 byte alignment
*
* All memory required by the manager is allocated at startup in
* the form of one large pool.
*
* Allocated blocks require a 4 byte header to store size, tag, and
* alignment information. Blocks that need to support the Z_TagFree()
* feature require an additional 8 byte link list structure.
*
* Free blocks require a 16 bytes of tracking information. If possible
* this information is stored directly in the block (which is in the
* pool.) If the free block is not large enough, its information is
* stored in an overflow buffer.
*
* In an effort to reduce fragmentation, blocks allocated for a short
* period of time at the end of the pool. All other blocks are allocated
* at the start. Allocation is first fit.
*
*/
#include "../game/q_shared.h"
#include "qcommon.h"
#include "../renderer/qgl_console.h"
#ifdef _GAMECUBE
#include <dolphin/os.h>
#endif
#ifdef _WINDOWS
#include <windows.h>
#endif
#ifdef _XBOX
#include <Xtl.h>
#endif
// Where do hunk allocations go?
static memtag_t hunk_tag;
// Used to mark the start and end of blocks in debug mode
#define ZONE_MAGIC 0xfe
// Size of the free block overflow buffer
#define ZONE_FREE_OVERFLOW 4096
// Indicates whether or not special (slow) debug code should be enabled
#define ZONE_DEBUG 0
// Allocate all available memory minus this amount
#ifdef _GAMECUBE
#define ZONE_HEAP_FREE_DEBUG (64*1024*4)
#define ZONE_HEAP_FREE_RELEASE (0)
#ifdef _DEBUG
# define ZONE_HEAP_FREE ZONE_HEAP_FREE_DEBUG
#else
# define ZONE_HEAP_FREE ZONE_HEAP_FREE_RELEASE
#endif
#else
//Game needs about 8 MB for framebuffers audio, bink, etc., plus 24 MB
//for textures.
//# define ZONE_HEAP_FREE (1024*1024*8 + 24*1024*1024)
# define ZONE_HEAP_FREE (1024*1024*8 + 24*1024*1024 + 4*1024*1024)
#endif
// Should we emulate the smaller memory footprint of actual release systems?
#define ZONE_EMULATE_SPACE 0
// All standard header data is crammed into 4 bytes
typedef unsigned int ZoneHeader;
// Debug markers to check for overflow/underflow
typedef unsigned int ZoneDebugHeader;
typedef unsigned char ZoneDebugFooter;
// Extended header information for memory freed with TagFree()
struct ZoneLinkHeader
{
ZoneLinkHeader* m_Next;
ZoneLinkHeader* m_Prev;
};
static ZoneLinkHeader* s_LinkBase;
// Free memory block tracking information
struct ZoneFreeBlock
{
unsigned int m_Address;
unsigned int m_Size;
ZoneFreeBlock* m_Next;
ZoneFreeBlock* m_Prev;
};
// Buffer to hold free memory information that we can't
// fit directly in the pool
static ZoneFreeBlock s_FreeOverflow[ZONE_FREE_OVERFLOW];
static int s_LastOverflowIndex;
static ZoneFreeBlock s_FreeStart;
static ZoneFreeBlock s_FreeEnd;
// Various stats collected at runtime
struct ZoneStats
{
int m_CountAlloc;
int m_SizeAlloc;
int m_OverheadAlloc;
int m_PeakAlloc;
int m_CountFree;
int m_SizeFree;
int m_SizesPerTag[TAG_COUNT];
int m_CountsPerTag[TAG_COUNT];
};
static ZoneStats s_Stats;
// Special empty block for zero size allocations
struct ZoneEmptyBlock
{
ZoneHeader header;
#ifdef _DEBUG
ZoneDebugHeader start;
ZoneDebugFooter end;
#endif
};
#ifdef _DEBUG
static ZoneEmptyBlock s_EmptyBlock = {TAG_STATIC << 25, ZONE_MAGIC, ZONE_MAGIC};
#else
static ZoneEmptyBlock s_EmptyBlock = {TAG_STATIC << 25};
#endif
// Free block jump table for fast memory deallocation
#define Z_JUMP_TABLE_SIZE 64
static ZoneFreeBlock* s_FreeJumpTable[Z_JUMP_TABLE_SIZE];
static unsigned int s_FreeJumpResolution;
static void* s_PoolBase;
static bool s_Initialized = false;
static bool s_IsNewDeleteTemp = false;
#ifndef _GAMECUBE
static HANDLE s_Mutex = INVALID_HANDLE_VALUE;
#endif
static void Z_Stats_f(void);
void Z_Details_f(void);
void Z_DumpMemMap_f(void);
#ifdef _XBOX
void ShowOSMemory(void)
{
MEMORYSTATUS stat;
GlobalMemoryStatus(&stat);
Com_Printf(" total mem: %d, free mem: %d\n", stat.dwTotalPhys / 1024,
stat.dwAvailPhys / 1024);
FILE *out = fopen("d:\\osmem.txt", "a");
if(out) {
fprintf(out, "total mem: %d, free mem: %d\n", stat.dwTotalPhys / 1024,
stat.dwAvailPhys / 1024);
fclose(out);
}
}
#endif
int Z_MemFree(void)
{
return s_Stats.m_SizeFree;
}
void Com_InitZoneMemory(void)
{
// assert(!s_Initialized);
// Zone now initializes on first use, can't reliably assume anything here
if (s_Initialized)
return;
Com_Printf("Initialising zone memory .....\n");
// Clear some globals
memset(&s_Stats, 0, sizeof(s_Stats));
memset(s_FreeOverflow, 0, sizeof(s_FreeOverflow));
s_LastOverflowIndex = 0;
s_LinkBase = NULL;
s_IsNewDeleteTemp = false;
// Alloc the pool
#if defined(_XBOX)
MEMORYSTATUS status;
GlobalMemoryStatus(&status);
// BTO : VVFIXME - Extra little note to see how much memory
// is being used by globals/statics
Com_Printf("*** PhysRAM: %d used, %d free\n",
status.dwTotalPhys-status.dwAvailPhys,
status.dwAvailPhys);
SIZE_T size;
# if ZONE_EMULATE_SPACE
#ifdef _DEBUG
//Emulated space is always about 6 megs off from release build. Try
//to compensate. This number may need tweaking in the future.
SIZE_T exe = 6500 * 1024;
#else
SIZE_T exe = 0; //Exe size is already reflected in GlobalMemoryStatus().
#endif
size = 0x4000000 - (exe + ZONE_HEAP_FREE);
# else
size = status.dwAvailPhys - ZONE_HEAP_FREE;
# endif
s_PoolBase = GlobalAlloc(0, size);
#elif defined(_WINDOWS)
SIZE_T size = 50*1024*1024;
s_PoolBase = GlobalAlloc(0, size);
#endif
// Setup the initial free block
ZoneFreeBlock* base = (ZoneFreeBlock*)s_PoolBase;
base->m_Address = (unsigned int)s_PoolBase;
base->m_Size = size;
base->m_Next = &s_FreeEnd;
base->m_Prev = &s_FreeStart;
// Init the free block jump table
memset(s_FreeJumpTable, 0, Z_JUMP_TABLE_SIZE * sizeof(ZoneFreeBlock*));
s_FreeJumpResolution = (size / Z_JUMP_TABLE_SIZE) + 1;
s_FreeJumpTable[0] = base;
// Setup free block dummies
s_FreeStart.m_Address = 0;
s_FreeStart.m_Size = 0;
s_FreeStart.m_Next = base;
s_FreeStart.m_Prev = NULL;
s_FreeEnd.m_Address = 0xFFFFFFFF;
s_FreeEnd.m_Size = 0;
s_FreeEnd.m_Next = NULL;
s_FreeEnd.m_Prev = base;
s_Stats.m_CountFree = 1;
s_Stats.m_SizeFree = size;
s_Initialized = true;
// Add some commands
Cmd_AddCommand("zone_stats", Z_Stats_f);
Cmd_AddCommand("zone_details", Z_Details_f);
Cmd_AddCommand("zone_memmap", Z_DumpMemMap_f);
#ifndef _GAMECUBE
s_Mutex = CreateMutex(NULL, FALSE, NULL);
#endif
}
void Com_ShutdownZoneMemory(void)
{
assert(s_Initialized);
// Remove commands
Cmd_RemoveCommand("zone_stats");
Cmd_RemoveCommand("zone_details");
Cmd_RemoveCommand("zone_memmap");
if (s_Stats.m_CountAlloc)
{
// Free all memory
// CM_ReleaseVisData();
Z_TagFree(TAG_ALL);
}
// Clear some globals
memset(&s_Stats, 0, sizeof(s_Stats));
memset(s_FreeOverflow, 0, sizeof(s_FreeOverflow));
s_LastOverflowIndex = 0;
s_LinkBase = NULL;
// Free the pool
#ifndef _GAMECUBE
GlobalFree(s_PoolBase);
CloseHandle(s_Mutex);
#endif
s_PoolBase = NULL;
s_Initialized = false;
}
// Determine if a tag should only be allocated for a very
// short period of time.
static bool Z_IsTagTemp(memtag_t eTag)
{
return
eTag == TAG_TEMP_WORKSPACE ||
#ifndef _JK2MP
eTag == TAG_TEMP_SAVEGAME_WORKSPACE ||
eTag == TAG_STRING ||
eTag == TAG_GP2 ||
#endif
eTag == TAG_SND_RAWDATA ||
eTag == TAG_ICARUS ||
#ifdef _JK2MP
eTag == TAG_TEXTPOOL ||
eTag == TAG_TEMP_HUNKALLOC ||
#endif
eTag == TAG_LISTFILES;
}
// Determine if a tag needs TagFree() support.
static bool Z_IsTagLinked(memtag_t eTag)
{
return
eTag == TAG_BSP ||
#ifndef _JK2MP
eTag == TAG_HUNKALLOC ||
eTag == TAG_HUNKMISCMODELS ||
eTag == TAG_G_ALLOC ||
#endif
#ifdef _JK2MP
eTag == TAG_CG_UI_ALLOC ||
eTag == TAG_BG_ALLOC ||
eTag == TAG_HUNK_MARK1 ||
eTag == TAG_HUNK_MARK2 ||
eTag == TAG_TEMP_HUNKALLOC ||
#endif
eTag == TAG_UI_ALLOC;
}
static int Z_CalcAlignmentPad(int iAlign, unsigned int iAddress, unsigned int iOffset,
unsigned int iSize, unsigned int iHeaderSize, unsigned int iFooterSize)
{
int align_size;
if (iAlign == 0) return 0;
if (iOffset == 0)
{
// Align data at low end of block
align_size = iAlign -
((iAddress + iHeaderSize) % iAlign);
}
else
{
// Align data at high end of block
unsigned int block_start = iAddress + iOffset -
iSize + iHeaderSize;
align_size = block_start % iAlign;
}
if (align_size == iAlign)
{
return 0;
}
return align_size;
}
static ZoneFreeBlock* Z_GetOverflowBlock(void)
{
for (int i = s_LastOverflowIndex; i < ZONE_FREE_OVERFLOW; ++i)
{
if (s_FreeOverflow[i].m_Address == 0)
{
s_LastOverflowIndex = i;
return &s_FreeOverflow[i];
}
}
for (int j = 0; j < s_LastOverflowIndex; ++j)
{
if (s_FreeOverflow[j].m_Address == 0)
{
s_LastOverflowIndex = j;
return &s_FreeOverflow[j];
}
}
return NULL;
}
static inline bool Z_IsFreeBlockLargeEnough(ZoneFreeBlock* pBlock, int iSize,
int iHeaderSize, int iFooterSize, int iAlign, bool bLow, int& iAlignPad)
{
// Is the block large enough?
if (pBlock->m_Size >= iSize)
{
if (iAlign > 0)
{
// If we need some aligment, we need to check size
// against that as well.
iAlignPad = Z_CalcAlignmentPad(iAlign,
pBlock->m_Address, !bLow ? pBlock->m_Size : 0,
iSize, iHeaderSize, iFooterSize);
if (pBlock->m_Size < iAlignPad + iSize)
{
return false;
}
}
return true;
}
return false;
}
static ZoneFreeBlock* Z_FindFirstFree(int iSize, int iHeaderSize,
int iFooterSize, int iAlign, int& iAlignPad)
{
for (ZoneFreeBlock* block = s_FreeStart.m_Next; block; block = block->m_Next)
{
if (Z_IsFreeBlockLargeEnough(block, iSize, iHeaderSize, iFooterSize,
iAlign, true, iAlignPad))
{
return block;
}
}
return NULL;
}
static ZoneFreeBlock* Z_FindLastFree(int iSize, int iHeaderSize,
int iFooterSize, int iAlign, int& iAlignPad)
{
for (ZoneFreeBlock* block = s_FreeEnd.m_Prev; block; block = block->m_Prev)
{
if (Z_IsFreeBlockLargeEnough(block, iSize, iHeaderSize, iFooterSize,
iAlign, false, iAlignPad))
{
return block;
}
}
return NULL;
}
static bool Z_ValidateFree(void)
{
#if ZONE_DEBUG
// Make sure no free blocks are overlapping
for (ZoneFreeBlock* a = &s_FreeStart; a; a = a->m_Next)
{
if (a->m_Address == 0 && a->m_Size != 0)
{
return false;
}
for (ZoneFreeBlock* b = &s_FreeStart; b; b = b->m_Next)
{
if (a != b &&
a->m_Address >= b->m_Address &&
a->m_Address < b->m_Address + b->m_Size)
{
return false;
}
}
}
#endif
return true;
}
static bool Z_ValidateLinks(void)
{
#if ZONE_DEBUG
// Make sure links are sane
for (ZoneLinkHeader* a = s_LinkBase; a; a = a->m_Next)
{
if ((a->m_Next && a != a->m_Next->m_Prev) ||
(a->m_Prev && a != a->m_Prev->m_Next))
{
return false;
}
}
#endif
return true;
}
static int Z_GetJumpTableIndex(unsigned int iAddress)
{
int index = (iAddress - (unsigned int)s_PoolBase) / s_FreeJumpResolution;
if (index < 0) return 0;
if (index >= Z_JUMP_TABLE_SIZE) return Z_JUMP_TABLE_SIZE - 1;
return index;
}
static ZoneFreeBlock* Z_GetFreeBlockBefore(unsigned int iAddress)
{
// Find this block's position in the jump table
int index = Z_GetJumpTableIndex(iAddress) - 1;
// Find a valid jump table entry
while (index >= 0 && !s_FreeJumpTable[index]) --index;
if (index < 0) return &s_FreeStart;
return s_FreeJumpTable[index];
}
static void Z_RemoveFromJumpTable(ZoneFreeBlock* pBlock)
{
// Is this block in the jump table?
int index = Z_GetJumpTableIndex(pBlock->m_Address);
if (s_FreeJumpTable[index] == pBlock)
{
// See if the next block will fit in our slot
if (pBlock->m_Next != &s_FreeEnd)
{
int nindex = Z_GetJumpTableIndex(pBlock->m_Next->m_Address);
if (nindex == index)
{
s_FreeJumpTable[index] = pBlock->m_Next;
return;
}
}
// See if the previous block will fit in our slot
if (pBlock->m_Prev != &s_FreeStart)
{
int pindex = Z_GetJumpTableIndex(pBlock->m_Prev->m_Address);
if (pindex == index)
{
s_FreeJumpTable[index] = pBlock->m_Prev;
return;
}
}
// No other free blocks fit here, give up
s_FreeJumpTable[index] = NULL;
}
}
static void Z_LinkFreeBlock(ZoneFreeBlock* pBlock)
{
ZoneFreeBlock* cur = Z_GetFreeBlockBefore(pBlock->m_Address);
for (; cur; cur = cur->m_Next)
{
// Find the correct position, ordered by address
if (cur->m_Address > pBlock->m_Address)
{
// Link up the block
pBlock->m_Next = cur;
pBlock->m_Prev = cur->m_Prev;
cur->m_Prev->m_Next = pBlock;
cur->m_Prev = pBlock;
// Update the jump table if necessary
int index = Z_GetJumpTableIndex(pBlock->m_Address);
if (!s_FreeJumpTable[index])
{
s_FreeJumpTable[index] = pBlock;
}
s_Stats.m_CountFree++;
s_Stats.m_SizeFree += pBlock->m_Size;
assert(Z_ValidateFree());
break;
}
}
}
static void* Z_SplitFree(ZoneFreeBlock* pBlock, int iSize, bool bLow)
{
assert(pBlock->m_Size >= iSize);
Z_RemoveFromJumpTable(pBlock);
// Delink the free block
ZoneFreeBlock fblock = *pBlock;
pBlock->m_Prev->m_Next = pBlock->m_Next;
pBlock->m_Next->m_Prev = pBlock->m_Prev;
pBlock->m_Address = 0;
s_Stats.m_CountFree--;
s_Stats.m_SizeFree -= pBlock->m_Size;
assert(Z_ValidateFree());
if (fblock.m_Size > iSize)
{
// Split the block into an allocated and free portion
int remainder = fblock.m_Size - iSize;
if (remainder < sizeof(ZoneFreeBlock))
{
// Free portion is not large to hold free info --
// we're going to have to use the overflow buffer.
ZoneFreeBlock* nblock = Z_GetOverflowBlock();
if (nblock == NULL)
{
Z_Details_f();
Com_Error(ERR_FATAL, "Zone free overflow buffer overflowed!");
}
// Split the block
void* ret;
if (bLow)
{
ret = (void*)fblock.m_Address;
nblock->m_Address = fblock.m_Address + iSize;
}
else
{
ret = (void*)(fblock.m_Address + remainder);
nblock->m_Address = fblock.m_Address;
}
nblock->m_Size = remainder;
Z_LinkFreeBlock(nblock);
return ret;
}
else
{
// Free portion is large enough -- split it
void* ret;
ZoneFreeBlock* nblock;
if (bLow)
{
ret = (void*)fblock.m_Address;
nblock = (ZoneFreeBlock*)(fblock.m_Address + iSize);
}
else
{
ret = (void*)(fblock.m_Address + remainder);
nblock = (ZoneFreeBlock*)fblock.m_Address;
}
nblock->m_Address = (unsigned int)nblock;
nblock->m_Size = remainder;
Z_LinkFreeBlock(nblock);
return ret;
}
}
else
{
// No need to split, just return block.
return (void*)fblock.m_Address;
}
}
static void Z_SetupAlignmentPad(void* pBlock, int iAlignPad, bool bLow)
{
// Clear alignment bytes
memset(pBlock, 0, iAlignPad);
// If we have more than 1 alignment byte, the first align byte
// tells us how many additional bytes we have.
if (iAlignPad > 1)
{
assert(iAlignPad < 256);
unsigned char* ptr;
if (bLow)
{
ptr = (unsigned char*)pBlock + (iAlignPad - 1);
}
else
{
ptr = (unsigned char*)pBlock;
}
*ptr = iAlignPad - 1;
}
}
void Z_MallocFail(const char* pMessage, int iSize, memtag_t eTag)
{
// Report the error
// Com_Printf("Z_Malloc(): %s : %d bytes and tag %d !!!!\n", pMessage, iSize, eTag);
Com_Printf("Z_Malloc(): %s : %d bytes and tag %d !!!!\n", pMessage, iSize, eTag);
Z_Details_f();
Z_DumpMemMap_f();
// Com_Printf("(Repeat): Z_Malloc(): %s : %d bytes and tag %d !!!!\n", pMessage, iSize, eTag);
Com_Printf("(Repeat): Z_Malloc(): %s : %d bytes and tag %d !!!!\n", pMessage, iSize, eTag);
// Clear the screen blue to indicate out of memory
for (;;)
{
qglBeginFrame();
qglClearColor(0, 0, 1, 1);
qglClear(GL_COLOR_BUFFER_BIT);
qglEndFrame();
}
}
void *Z_Malloc(int iSize, memtag_t eTag, qboolean bZeroit, int iAlign)
{
// assert(s_Initialized);
// Zone now initializes on first use. (During static constructors)
if (!s_Initialized)
Com_InitZoneMemory();
if (iSize == 0)
{
#ifdef _DEBUG
return (void*)(&s_EmptyBlock.start + 1);
#else
return (void*)(&s_EmptyBlock.header + 1);
#endif
}
if (iSize < 0)
{
Z_MallocFail("Negative size", iSize, eTag);
return NULL;
}
#ifndef _GAMECUBE
WaitForSingleObject(s_Mutex, INFINITE);
#endif
// Make new/delete memory temporary if requested
if (eTag == TAG_NEWDEL && s_IsNewDeleteTemp)
{
eTag = TAG_TEMP_WORKSPACE;
}
// Determine how much space we need with headers and footers
int header_size = sizeof(ZoneHeader);
int footer_size = 0;
if (Z_IsTagLinked(eTag))
{
header_size += sizeof(ZoneLinkHeader);
}
#ifdef _DEBUG
header_size += sizeof(ZoneDebugHeader);
footer_size += sizeof(ZoneDebugFooter);
#endif
int real_size = iSize + header_size + footer_size;
int align_pad = 0;
// Get a bit of free memory. Temporary memory is allocated
// from the end. More permanent allocations are done at the
// begining of the pool.
ZoneFreeBlock* fblock;
if (Z_IsTagTemp(eTag))
{
fblock = Z_FindLastFree(real_size, header_size, footer_size,
iAlign, align_pad);
}
else
{
fblock = Z_FindFirstFree(real_size, header_size, footer_size,
iAlign, align_pad);
}
// Did we actually find some memory?
if (!fblock)
{
#ifndef _GAMECUBE
ReleaseMutex(s_Mutex);
#endif
// if(eTag == TAG_TEMP_SND_RAWDATA) {
if(eTag == TAG_SND_RAWDATA) {
return NULL;
}
Z_MallocFail("Out of memory", iSize, eTag);
return NULL;
}
// Add any alignment bytes
real_size += align_pad;
// Split the free block and get a pointer to the start
// allocated space.
void* ablock;
if (Z_IsTagTemp(eTag))
{
ablock = Z_SplitFree(fblock, real_size, false);
// Append align pad to end of block
Z_SetupAlignmentPad(
(void*)((char*)ablock + real_size - align_pad),
align_pad, false);
}
else
{
ablock = Z_SplitFree(fblock, real_size, true);
// Insert align pad at block start
Z_SetupAlignmentPad(ablock, align_pad, true);
ablock = (void*)((char*)ablock + align_pad);
}
if (!ablock)
{
Z_MallocFail("Failed to split", iSize, eTag);
}
// Add linking header if necessary
if (Z_IsTagLinked(eTag))
{
ZoneLinkHeader* linked = (ZoneLinkHeader*)ablock;
linked->m_Next = s_LinkBase;
linked->m_Prev = NULL;
if (s_LinkBase)
{
s_LinkBase->m_Prev = linked;
}
s_LinkBase = linked;
assert(Z_ValidateLinks());
// Next...
ablock = (void*)((char*)ablock + sizeof(ZoneLinkHeader));
}
// Setup the header:
// 31 - alignment flag
// 25-30 - tag
// 0-24 - size without headers/footers
assert(iSize >= 0 && iSize < (1 << 25));
assert(eTag >= 0 && eTag < 64);
ZoneHeader* header = (ZoneHeader*)ablock;
*header =
(((unsigned int)eTag) << 25) |
((unsigned int)iSize);
if (align_pad)
{
*header |= (1 << 31);
}
// Next...
ablock = (void*)((char*)ablock + sizeof(ZoneHeader));
#ifdef _DEBUG
{
// Setup the debug markers
ZoneDebugHeader* debug_header = (ZoneDebugHeader*)ablock;
ZoneDebugFooter* debug_footer = (ZoneDebugFooter*)((char*)debug_header +
(sizeof(ZoneDebugHeader) + iSize));
*debug_header = ZONE_MAGIC;
*debug_footer = ZONE_MAGIC;
// Next...
ablock = (void*)((char*)ablock + sizeof(ZoneDebugHeader));
}
#endif
// Update the stats
s_Stats.m_SizeAlloc += iSize;
s_Stats.m_OverheadAlloc += header_size + footer_size + align_pad;
s_Stats.m_SizesPerTag[eTag] += iSize;
s_Stats.m_CountAlloc++;
s_Stats.m_CountsPerTag[eTag]++;
if (s_Stats.m_SizeAlloc + s_Stats.m_OverheadAlloc > s_Stats.m_PeakAlloc)
{
s_Stats.m_PeakAlloc = s_Stats.m_SizeAlloc + s_Stats.m_OverheadAlloc;
}
// Return a pointer to data memory
if (bZeroit)
{
memset(ablock, 0, iSize);
}
assert(iAlign == 0 || (unsigned int)ablock % iAlign == 0);
/*
This is useful for figuring out who's allocating a certain block of
memory. Please don't remove it.
if(eTag == TAG_NEWDEL && (unsigned int)ablock >= 0x806c0000 &&
(unsigned int)ablock <= 0x806c1000 && iSize == 24) {
int suck = 0;
}
if(eTag == TAG_SMALL && (iSize == 7 || iSize == 96)) {
int suck = 0;
}
if(eTag == TAG_CLIENTS) {
int suck = 0;
}
*/
#ifndef _GAMECUBE
ReleaseMutex(s_Mutex);
#endif
return ablock;
}
static memtag_t Z_GetTag(const ZoneHeader* header)
{
return (*header & 0x7E000000) >> 25;
}
static unsigned int Z_GetSize(const ZoneHeader* header)
{
return *header & 0x1FFFFFF;
}
static int Z_GetAlign(const ZoneHeader* header)
{
if (*header & (1 << 31))
{
unsigned char* ptr = (unsigned char*)header;
memtag_t tag = Z_GetTag(header);
// point to the first alignment block
if (Z_IsTagTemp(tag))
{
ptr += sizeof(ZoneHeader) + Z_GetSize(header);
#ifdef _DEBUG
ptr += sizeof(ZoneDebugHeader) + sizeof(ZoneDebugFooter);
#endif
}
else
{
if (Z_IsTagLinked(tag))
{
// skip the link header
ptr -= sizeof(ZoneLinkHeader);
}
ptr -= 1;
}
return *ptr + 1;
}
return 0;
}
int Z_Size(void *pvAddress)
{
assert(s_Initialized);
#ifdef _DEBUG
ZoneDebugHeader* debug = (ZoneDebugHeader*)pvAddress - 1;
if (*debug != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Size(): Not a valid zone header!");
return 0; // won't get here
}
pvAddress = (void*)debug;
#endif
ZoneHeader* header = (ZoneHeader*)pvAddress - 1;
if (Z_GetTag(header) == TAG_STATIC)
{
return 0; // kind of
}
return Z_GetSize(header);
}
static void Z_Coalasce(ZoneFreeBlock* pBlock)
{
unsigned int size = 0;
// Find later free blocks adjacent to us
ZoneFreeBlock* end;
for (end = pBlock->m_Next;
end->m_Next;
end = end->m_Next)
{
if (end->m_Address !=
end->m_Prev->m_Address + end->m_Prev->m_Size)
{
break;
}
size += end->m_Size;
Z_RemoveFromJumpTable(end);
end->m_Address = 0; // invalidate block
s_Stats.m_CountFree--;
}
// Find previous free blocks adjacent to us
ZoneFreeBlock* start;
for (start = pBlock;
start->m_Prev;
start = start->m_Prev)
{
if (start->m_Prev->m_Address + start->m_Prev->m_Size !=
start->m_Address)
{
break;
}
size += start->m_Size;
Z_RemoveFromJumpTable(start);
start->m_Address = 0; // invalidate block
s_Stats.m_CountFree--;
}
// Do we need to coalesce some blocks?
if (start->m_Next != end)
{
start->m_Next = end;
end->m_Prev = start;
start->m_Size += size;
}
}
// Return type of Z_Free differs in SP/MP. Macro hack to wrap it up
#ifdef _JK2MP
void Z_Free(void *pvAddress)
#define Z_FREE_RETURN(x) return
#else
int Z_Free(void *pvAddress)
#define Z_FREE_RETURN(x) return (x)
#endif
{
#ifdef _WINDOWS
if (!s_Initialized) return;
#endif
assert(s_Initialized);
#ifdef _DEBUG
// check the header magic
ZoneDebugHeader* debug_header = (ZoneDebugHeader*)pvAddress - 1;
if (*debug_header != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!");
Z_FREE_RETURN( 0 );
}
ZoneHeader* header = (ZoneHeader*)debug_header - 1;
// check the footer magic
ZoneDebugFooter* debug_footer = (ZoneDebugFooter*)((char*)pvAddress +
Z_GetSize(header));
if (*debug_footer != ZONE_MAGIC)
{
Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone footer!");
Z_FREE_RETURN( 0 );
}
#else
ZoneHeader* header = (ZoneHeader*)pvAddress - 1;
#endif
memtag_t tag = Z_GetTag(header);
if (tag != TAG_STATIC)
{
#ifndef _GAMECUBE
WaitForSingleObject(s_Mutex, INFINITE);
#endif
// Determine size of header and footer
int header_size = sizeof(ZoneHeader);
int align_size = Z_GetAlign(header);
int footer_size = 0;
int data_size = Z_GetSize(header);
if (Z_IsTagLinked(tag))
{
header_size += sizeof(ZoneLinkHeader);
}
if (Z_IsTagTemp(tag))
{
footer_size += align_size;
}
else
{
header_size += align_size;
}
#ifdef _DEBUG
header_size += sizeof(ZoneDebugHeader);
footer_size += sizeof(ZoneDebugFooter);
#endif
int real_size = data_size + header_size + footer_size;
// Update the stats
s_Stats.m_SizeAlloc -= data_size;
s_Stats.m_OverheadAlloc -= header_size + footer_size;
s_Stats.m_SizesPerTag[tag] -= data_size;
s_Stats.m_CountAlloc--;
s_Stats.m_CountsPerTag[tag]--;
// Delink block
if (Z_IsTagLinked(tag))
{
ZoneLinkHeader* linked = (ZoneLinkHeader*)header - 1;
if (linked == s_LinkBase)
{
s_LinkBase = linked->m_Next;
if (s_LinkBase)
{
s_LinkBase->m_Prev = NULL;
}
}
else
{
if (linked->m_Next)
{
linked->m_Next->m_Prev = linked->m_Prev;
}
linked->m_Prev->m_Next = linked->m_Next;
}
assert(Z_ValidateLinks());
}
// Clear the block header for safety
*header = 0;
// Add block to free list
ZoneFreeBlock* nblock = NULL;
if (real_size < sizeof(ZoneFreeBlock))
{
// Not enough space in block to put free information --
// use overflow buffer.
nblock = Z_GetOverflowBlock();
if (nblock == NULL)
{
Z_Details_f();
Com_Error(ERR_FATAL, "Zone free overflow buffer overflowed!");
}
}
else
{
// Place free information in block
nblock = (ZoneFreeBlock*)((char*)pvAddress - header_size);
}
nblock->m_Address = (unsigned int)pvAddress - header_size;
nblock->m_Size = real_size;
Z_LinkFreeBlock(nblock);
// Coalesce any adjacent free blocks
Z_Coalasce(nblock);
#ifndef _GAMECUBE
ReleaseMutex(s_Mutex);
#endif
}
Z_FREE_RETURN( 0 );
}
int Z_MemSize(memtag_t eTag)
{
return s_Stats.m_SizesPerTag[eTag];
}
#if ZONE_DEBUG
void Z_FindLeak(void)
{
assert(s_Initialized);
static int cycle_count = 0;
const memtag_t tag = TAG_NEWDEL;
struct PointerInfo
{
void* data;
int counter;
bool mark;
};
const int max_pointers = 32768;
static PointerInfo pointers[max_pointers];
static int num_pointers = 0;
// Clear pointer existance
for (int i = 0; i < num_pointers; ++i)
{
pointers[i].mark = false;
}
// Add all known pointers
int start_num = num_pointers;
for (ZoneLinkHeader* link = s_LinkBase; link;)
{
ZoneHeader* header = (ZoneHeader*)(link + 1);
link = link->m_Next;
if (Z_GetTag(header) == tag)
{
// See if the pointer already is in the array
bool found = false;
for (int k = start_num; k < num_pointers; ++k)
{
if (pointers[k].data == header)
{
++pointers[k].counter;
pointers[k].mark = true;
found = true;
break;
}
}
// If the pointer is not in the array, add it
if (!found)
{
assert(num_pointers < max_pointers);
pointers[num_pointers].data = header;
pointers[num_pointers].counter = 0;
pointers[num_pointers].mark = true;
++num_pointers;
}
}
}
// Remove pointers that are no longer used
for (int j = 0; j < num_pointers; ++j)
{
if (pointers[j].mark)
{
if (pointers[j].counter != cycle_count &&
pointers[j].counter != cycle_count - 1 &&
pointers[j].counter != 0)
{
Com_Printf("Memory leak: %p\n", pointers[j].data);
}
}
else
{
int k;
for (k = j; k < num_pointers; ++k)
{
if (pointers[k].mark) break;
}
if (k == num_pointers) break;
memmove(pointers + j, pointers + k, (num_pointers - k) * sizeof(PointerInfo));
num_pointers -= k - j;
}
}
++cycle_count;
}
#endif
void Z_TagPointers(memtag_t eTag)
{
assert(s_Initialized);
#ifndef _GAMECUBE
WaitForSingleObject(s_Mutex, INFINITE);
#endif
Com_Printf("Pointers for tag %d:\n", eTag);
for (ZoneLinkHeader* link = s_LinkBase; link;)
{
ZoneHeader* header = (ZoneHeader*)(link + 1);
link = link->m_Next;
if (eTag == TAG_ALL || Z_GetTag(header) == eTag)
{
#ifdef _DEBUG
Com_Printf("%x - %d\n", ((void*)((char*)header +
sizeof(ZoneHeader) + sizeof(ZoneDebugHeader))),
Z_Size(((void*)((char*)header +
sizeof(ZoneHeader) + sizeof(ZoneDebugHeader)))));
#else
Com_Printf("%x - %d\n", (void*)(header + 1),
Z_Size((void*)(header + 1)));
#endif
}
}
#ifndef _GAMECUBE
ReleaseMutex(s_Mutex);
#endif
}
void Z_TagFree(memtag_t eTag)
{
assert(s_Initialized);
for (ZoneLinkHeader* link = s_LinkBase; link;)
{
ZoneHeader* header = (ZoneHeader*)(link + 1);
link = link->m_Next;
if (eTag == TAG_ALL || Z_GetTag(header) == eTag)
{
#ifdef _DEBUG
Z_Free((void*)((char*)header + sizeof(ZoneHeader) +
sizeof(ZoneDebugHeader)));
#else
Z_Free((void*)(header + 1));
#endif
}
}
}
void Z_SetNewDeleteTemporary(bool bTemp)
{
// Catch nested uses that break when unwinding the stack
assert(bTemp != s_IsNewDeleteTemp);
s_IsNewDeleteTemp = bTemp;
}
void *S_Malloc( int iSize )
{
return Z_Malloc(iSize, TAG_SMALL, qfalse, 0);
}
int Z_GetLevelMemory(void)
{
#ifdef _JK2MP
return s_Stats.m_SizesPerTag[TAG_BSP];
#else
return s_Stats.m_SizesPerTag[TAG_HUNKALLOC] +
s_Stats.m_SizesPerTag[TAG_HUNKMISCMODELS] +
s_Stats.m_SizesPerTag[TAG_BSP];
#endif
}
#ifdef _JK2MP
int Z_GetHunkMemory(void)
{
return s_Stats.m_SizesPerTag[TAG_HUNK_MARK1] +
s_Stats.m_SizesPerTag[TAG_HUNK_MARK2] +
s_Stats.m_SizesPerTag[TAG_TEMP_HUNKALLOC];
}
#endif
int Z_GetTerrainMemory(void)
{
return s_Stats.m_SizesPerTag[TAG_CM_TERRAIN] +
s_Stats.m_SizesPerTag[TAG_CM_TERRAIN_TEMP] +
#ifdef _JK2MP
s_Stats.m_SizesPerTag[TAG_TERRAIN] +
#endif
s_Stats.m_SizesPerTag[TAG_R_TERRAIN];
}
int Z_GetMiscMemory(void)
{
return s_Stats.m_SizeAlloc -
(Z_GetLevelMemory() +
#ifdef _JK2MP
Z_GetHunkMemory() +
#endif
Z_GetTerrainMemory() +
s_Stats.m_SizesPerTag[TAG_MODEL_GLM] +
s_Stats.m_SizesPerTag[TAG_MODEL_GLA] +
s_Stats.m_SizesPerTag[TAG_MODEL_MD3] +
s_Stats.m_SizesPerTag[TAG_BINK] +
s_Stats.m_SizesPerTag[TAG_SND_RAWDATA]);
}
#ifdef _GAMECUBE
static int texMemSize = 0;
#else
extern int texMemSize;
#endif
void Z_CompactStats(void)
{
assert(s_Initialized);
//This report is conservative. Divides by 1000 instead of 1024 and
//then rounds up.
Sys_Log("memory-map.txt", va("**Z_CompactStats Start**\n"));
Sys_Log("memory-map.txt", va("map: %s\n", Cvar_VariableString( "mapname" )) );
Sys_Log("memory-map.txt", va("OV: %d, LVL: %d, GLM: %d, GLA: %d, MD3: %d\n",
(s_Stats.m_OverheadAlloc / 1000) + 1,
(Z_GetLevelMemory() / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_MODEL_GLM] / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_MODEL_GLA] / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_MODEL_MD3] / 1000) + 1));
Sys_Log("memory-map.txt", va("TER: %d, SND: %d, TEX: %d, FMV: %d, MSC: %d\n",
(Z_GetTerrainMemory() / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_SND_RAWDATA] / 1000) + 1,
(texMemSize / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_BINK] / 1000) + 1,
(Z_GetMiscMemory() / 1000) + 1));
#ifdef _JK2MP
Sys_Log("memory-map.txt", va("HUNK: %d, THUNK: %d\n",
((s_Stats.m_SizesPerTag[TAG_HUNK_MARK1] + s_Stats.m_SizesPerTag[TAG_HUNK_MARK2]) / 1000) + 1,
(s_Stats.m_SizesPerTag[TAG_TEMP_HUNKALLOC] / 1000) + 1));
#endif
Sys_Log("memory-map.txt", va("Free Zone: %d\n", s_Stats.m_SizeFree));
}
static void Z_Stats_f(void)
{
assert(s_Initialized);
// Display some memory usage summary information...
Com_Printf("\nThe zone is using %d bytes (%.2fMB) in %d memory blocks\n",
s_Stats.m_SizeAlloc,
(float)s_Stats.m_SizeAlloc / 1024.0f / 1024.0f,
s_Stats.m_CountAlloc);
Com_Printf("Free memory is %d bytes (%.2fMB) in %d memory blocks\n",
s_Stats.m_SizeFree,
(float)s_Stats.m_SizeFree / 1024.0f / 1024.0f,
s_Stats.m_CountFree);
Com_Printf("The zone peaked at %d bytes (%.2fMB)\n",
s_Stats.m_PeakAlloc,
(float)s_Stats.m_PeakAlloc / 1024.0f / 1024.0f);
Com_Printf("The zone overhead is %d bytes (%.2fMB)\n",
s_Stats.m_OverheadAlloc,
(float)s_Stats.m_OverheadAlloc / 1024.0f / 1024.0f);
}
void Z_Details_f(void)
{
assert(s_Initialized);
// Display some tag specific information...
Com_Printf("---------------------------------------------------------------------------\n");
Com_Printf("%20s %9s\n","Zone Tag","Bytes");
Com_Printf("%20s %9s\n","--------","-----");
for (int i=0; i<TAG_COUNT; i++)
{
int iThisCount = s_Stats.m_CountsPerTag[i];
int iThisSize = s_Stats.m_SizesPerTag[i];
if (iThisCount)
{
float fSize = (float)(iThisSize) / 1024.0f / 1024.0f;
int iSize = fSize;
int iRemainder = 100.0f * (fSize - floor(fSize));
Com_Printf("%d %9d (%2d.%02dMB) in %6d blocks (%9d average)\n",
i, iThisSize, iSize, iRemainder, iThisCount, iThisSize / iThisCount);
}
}
Com_Printf("---------------------------------------------------------------------------\n");
Z_Stats_f();
}
void Z_DumpMemMap_f(void)
{
# define WRITECHAR(C) \
Sys_Log("memmap.txt", C, 1, false); \
cur += 1024; \
if ((++counter) % 81 == 0) Sys_Log("memmap.txt", "\n", 1, false);
unsigned int cur = (unsigned int)s_PoolBase;
unsigned int counter = 0;
for (ZoneFreeBlock* fblock = &s_FreeStart; fblock != &s_FreeEnd; fblock = fblock->m_Next)
{
while (fblock->m_Address > cur + 1024)
{
WRITECHAR("*");
}
if (fblock->m_Address > cur && fblock->m_Address < cur + 1024)
{
WRITECHAR("+");
}
while (fblock->m_Address + fblock->m_Size > cur + 1024)
{
WRITECHAR("-");
}
if (fblock->m_Address + fblock->m_Size > cur &&
fblock->m_Address + fblock->m_Size < cur + 1024)
{
WRITECHAR("+");
}
}
Sys_Log("memmap.txt", "\n");
}
void Z_DisplayLevelMemory(int size, int surf, int block)
{
Z_DumpMemMap_f();
//Yes, it should be divided by 1024, but I'm going for a safety margin
//by rounding down.
//Com_Printf("level memory used: %d KB\n", size / 1000);
//Z_CompactStats(size, surf, block);
Z_CompactStats();
}
void Z_DisplayLevelMemory(void)
{
#ifdef _GAMECUBE
extern void R_SurfMramUsed(int &surface, int &block);
int surface, block;
R_SurfMramUsed(surface, block);
Z_DisplayLevelMemory(Z_GetLevelMemory(), surface, block);
#else
Z_DisplayLevelMemory(Z_GetLevelMemory(), 0, 0);
#endif
}
/*
========================
CopyString
NOTE: never write over the memory CopyString returns because
memory from a memstatic_t might be returned
========================
*/
char *CopyString( const char *in )
{
struct ZoneSingleChar
{
ZoneHeader header;
#ifdef _DEBUG
ZoneDebugHeader start;
#endif
char data[2];
#ifdef _DEBUG
ZoneDebugFooter end;
#endif
};
#ifdef _DEBUG
static ZoneSingleChar empty = {(TAG_STATIC << 25) | 2, ZONE_MAGIC, "\0", ZONE_MAGIC};
static ZoneSingleChar numbers[10] =
{
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "0", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "1", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "2", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "3", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "4", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "5", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "6", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "7", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "8", ZONE_MAGIC},
{(TAG_STATIC << 25) | 2, ZONE_MAGIC, "9", ZONE_MAGIC},
};
#else
static ZoneSingleChar empty = {(TAG_STATIC << 25) | 2, "\0"};
static ZoneSingleChar numbers[10] =
{
{(TAG_STATIC << 25) | 2, "0"},
{(TAG_STATIC << 25) | 2, "1"},
{(TAG_STATIC << 25) | 2, "2"},
{(TAG_STATIC << 25) | 2, "3"},
{(TAG_STATIC << 25) | 2, "4"},
{(TAG_STATIC << 25) | 2, "5"},
{(TAG_STATIC << 25) | 2, "6"},
{(TAG_STATIC << 25) | 2, "7"},
{(TAG_STATIC << 25) | 2, "8"},
{(TAG_STATIC << 25) | 2, "9"},
};
#endif
char *out;
if (!in[0])
{
return empty.data;
}
else if (!in[1])
{
if (in[0] >= '0' && in[0] <= '9')
{
return numbers[in[0]-'0'].data;
}
}
out = (char *) S_Malloc (strlen(in)+1);
strcpy (out, in);
// Z_Label(out,in);
return out;
}
void Com_TouchMemory(void)
{
// Stub function. Do nothing.
return;
}
qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag)
{
assert(s_Initialized);
#ifdef _DEBUG
ZoneDebugHeader* debug = (ZoneDebugHeader*)pvAddress - 1;
if (*debug != ZONE_MAGIC)
{
return qfalse;
}
pvAddress = (void*)debug;
#endif
ZoneHeader* header = (ZoneHeader*)pvAddress - 1;
if (Z_GetTag(header) != eTag)
{
return qfalse;
}
return Z_GetSize(header);
}
/*
Hunk emulation - PC switched to system similar to ours. I made the remaining
changes so that the two are identical.
*/
#ifdef _JK2MP
qboolean Com_TheHunkMarkHasBeenMade(void)
{
if (hunk_tag == TAG_HUNK_MARK2)
{
return qtrue;
}
return qfalse;
}
/*
=================
Com_InitHunkMemory
=================
*/
void Com_InitHunkMemory(void)
{
hunk_tag = TAG_HUNK_MARK1;
Hunk_Clear();
}
/*
====================
Hunk_MemoryRemaining
====================
*/
int Hunk_MemoryRemaining(void)
{
return 0;
}
/*
===================
Hunk_SetMark
The server calls this after the level and game VM have been loaded
===================
*/
void Hunk_SetMark(void)
{
hunk_tag = TAG_HUNK_MARK2;
}
/*
=================
Hunk_ClearToMark
The client calls this before starting a vid_restart or snd_restart
=================
*/
void Hunk_ClearToMark(void)
{
assert(hunk_tag == TAG_HUNK_MARK2); //if this is not true then no mark has been made
Z_TagFree(TAG_HUNK_MARK2);
}
/*
=================
Hunk_CheckMark
=================
*/
qboolean Hunk_CheckMark( void )
{
if (hunk_tag != TAG_HUNK_MARK1)
{
return qtrue;
}
return qfalse;
}
/*
=================
Hunk_Clear
The server calls this before shutting down or loading a new map
VVFIXME - PC version does lots of other things in here.
=================
*/
void R_HunkClearCrap(void);
void Hunk_Clear(void)
{
hunk_tag = TAG_HUNK_MARK1;
Z_TagFree(TAG_HUNK_MARK1);
Z_TagFree(TAG_HUNK_MARK2);
R_HunkClearCrap();
/*
Z_TagFree(TAG_HUNKALLOC);
Z_TagFree(TAG_BSP_HUNK);
Z_TagFree(TAG_BOT_HUNK);
Z_TagFree(TAG_RENDERER_HUNK);
Z_TagFree(TAG_SKELETON);
Z_TagFree(TAG_MODEL_OTHER);
Z_TagFree(TAG_MODEL_CHAR);
VM_Clear();
*/
}
/*
=================
Hunk_Alloc
Allocate permanent (until the hunk is cleared) memory
=================
*/
void *Hunk_Alloc(int size, ha_pref preference)
{
return Z_Malloc(size, hunk_tag, qtrue);
}
/*
=================
Hunk_AllocateTempMemory
This is used by the file loading system.
Multiple files can be loaded in temporary memory.
When the files-in-use count reaches zero, all temp memory will be deleted
=================
*/
void *Hunk_AllocateTempMemory(int size)
{
// don't bother clearing, because we are going to load a file over it
return Z_Malloc(size, TAG_TEMP_HUNKALLOC, qfalse);
}
/*
==================
Hunk_FreeTempMemory
==================
*/
void Hunk_FreeTempMemory(void *buf)
{
Z_Free(buf);
}
/*
=================
Hunk_ClearTempMemory
The temp space is no longer needed. If we have left more
touched but unused memory on this side, have future
permanent allocs use this side.
=================
*/
void Hunk_ClearTempMemory(void)
{
Z_TagFree(TAG_TEMP_HUNKALLOC);
}
#endif // _JK2MP
/*
XTL Replacement functions
XMemAlloc
XMemFree
XMemSize
Replacing these lets us intercept ALL memory allocation done by the XTL, and lets the
Zone take pretty much all available memory at startup
*/
/* This still doesn't work. Numrous allocations still use internal functions, so there's
little benefit right now.
XBOXAPI
LPVOID
WINAPI
XMemAlloc(SIZE_T dwSize, DWORD dwAllocAttributes)
{
// We always give XTL 16 byte aligned memory
return Z_Malloc(dwSize, TAG_XTL, ((PXALLOC_ATTRIBUTES)&dwAllocAttributes)->dwZeroInitialize, 16);
}
XBOXAPI
VOID
WINAPI
XMemFree(PVOID pAddress, DWORD dwAllocAttributes)
{
Z_Free(pAddress);
}
XBOXAPI
SIZE_T
WINAPI
XMemSize(PVOID pAddress, DWORD dwAllocAttributes)
{
return Z_Size(pAddress);
}
*/