// CryMemoryManager.cpp : Defines the entry point for the DLL application. #include "stdafx.h" #include #include #include #include #include "platform.h" #undef USE_NEWPOOL #include ////////////////////////////////////////////////////////////////////////// // Some globals for fast profiling. ////////////////////////////////////////////////////////////////////////// size_t g_TotalAllocatedMemory = 0; size_t g_ScriptAllocatedMemory = 0; int g_nPrecaution = 0; // will cause delayed crash, will make engine extremally unstable. //#if !defined(LINUX) extern ISystem* g_System; extern bool g_bProfilerEnabled; ////////////////////////////////////////////////////////////////////////// // Undefine malloc for memory manager itself.. #undef malloc #undef realloc #undef free #ifndef _XBOX #define GLOBALPOOLSIZE (32*1025*1025+16) #define EXTRAPOOLSIZE (32*1025*1025+16) // how much to allocate if we allow the pool to be exceeded // currently biggest allocation happends during hires screenshot creation #else // _XBOX #ifndef _DEBUG #define GLOBALPOOLSIZE (8*1025*1025+16) #define EXTRAPOOLSIZE (8*1025*1025+16) #else // _DEBUG #define GLOBALPOOLSIZE (0) #define EXTRAPOOLSIZE (0) #endif // _DEBUG #endif // _XBOX #define MAXPOOLS 128 void *poolbufs[MAXPOOLS]; int poolsizes[MAXPOOLS]; int numpools = 0; #if defined(WIN32) || defined(LINUX) #if defined(WIN64) || defined(LINUX64) // non-512 doesn't work for some reason: an infinite loop (recursion with stack overflow) occurs #define BUCKETQUANT 512 // wouter: affects performance of bucket allocator, modify with care! #else #define BUCKETQUANT 512*2 #endif #else #define BUCKETQUANT 256 // save some memory overhead with tiny speed cost on consoles #endif /* PoolContext *AllocPool(PoolContext *pCtx, int size) { // TODO: replace "malloc" with however we obtain memory on other platforms void *buf = VirtualAlloc(NULL,size,MEM_COMMIT,PAGE_READWRITE); if(!buf) { CryError( " (AllocPool) malloc() Failed" ); }; bpool(pCtx, buf, size); if (numpools==MAXPOOLS) { CryError( " (AllocPool) Maximum number of memory pools reached." ); } poolsizes[numpools] = size; poolbufs[numpools++] = buf; return pCtx; }; */ //static PoolContext globalpool; //static PoolContext *g_pool = AllocPool(InitPoolContext(&globalpool), GLOBALPOOLSIZE); static unsigned int biggestalloc = 0; /* #define MAXSTAT 1000 static int stats[MAXSTAT]; void addstat(int size) { if(size<0 || size>=MAXSTAT) size = MAXSTAT-1; stats[size]++; }; void printstats() { for(int i = 0; i %d\n", i, stats[i]); ::OutputDebugString(buf); }; }; int clearstats() { for(int i = 0; i>PTRBITS; }; int *ppage(void *p) { return (int *)(((INT_PTR)p)&PAGEMASK); }; enum { PTRBITS = PTRSIZE==2 ? 1 : PTRSIZE==4 ? 2 : 3 }; void *reuse[MAXBUCKETS]; void **pages; //! Total allocated size. void putinbuckets(char *start, char *end, int bsize) { int size = bsize*PTRSIZE; for(end -= size; start<=end; start += size) { *((void **)start) = reuse[bsize]; reuse[bsize] = start; }; }; void newpageblocks() { char *b = (char *)::malloc(PAGEBLOCKSIZE); // if we could get page aligned memory here, that would be even better char *first = ((char *)ppage(b))+PAGESIZE; for(int i = 0; iBUCKETQUANT) bpool(g_pool, first+PAGEBLOCKSIZE-PAGESIZE, b-first+PAGESIZE); }; void *newpage(unsigned int bsize) { if(!pages) newpageblocks(); void **page = pages; pages = (void **)*pages; *page = 0; putinbuckets((char *)(page+1), ((char *)page)+PAGESIZE, bsize); return alloc(bsize*PTRSIZE); }; void freepage(int *page, int bsize) // worst case if very large amounts of objects get deallocated in random order from when they were allocated { for(void **r = &reuse[bsize]; *r; ) { if(page == ppage(*r)) *r = *((void **)*r); else r = (void **)*r; }; void **p = (void **)page; *p = pages; pages = p; }; public: PageBucketAllocator() { pages = NULL; for(int i = 0; ibiggestalloc) { biggestalloc = size; }; if(size>MAXREUSESIZE) return ::malloc(size); size = bucket(size); void **r = (void **)reuse[size]; if(!r) return newpage(size); reuse[size] = *r; int *page = ppage(r); (*page)++; return (void *)r; }; void dealloc(void *p, unsigned int size) { if(size>MAXREUSESIZE) { ::free(p); //brel(g_pool, p); } else { size = bucket(size); *((void **)p) = reuse[size]; reuse[size] = p; int *page = ppage(p); if(!--(*page)) freepage(page, size); }; }; void stats() { int totalwaste = 0; char buf[100]; for(int i = 0; i %d (%d k)\n", i*4, n, waste); ::OutputDebugString(buf); }; }; sprintf(buf, "totalwaste %d k\n", totalwaste); ::OutputDebugString(buf); }; }; PageBucketAllocator g_GlobPageBucketAllocator; CRYMEMORYMANAGER_API void *CryMalloc(size_t size) { if (!g_bProfilerEnabled) { g_TotalAllocatedMemory += size+sizeof(int); int *p = (int *)g_GlobPageBucketAllocator.alloc(size+sizeof(int)); *p++ = size; // stores 2 sizes for big objects! return p; } else { FUNCTION_PROFILER_FAST( g_System,PROFILE_SYSTEM,g_bProfilerEnabled ); g_TotalAllocatedMemory += size+sizeof(int); int *p = (int *)g_GlobPageBucketAllocator.alloc(size+sizeof(int)); *p++ = size; // stores 2 sizes for big objects! return p; } } CRYMEMORYMANAGER_API void CryFree(void *p) { if (!g_bProfilerEnabled) { if (p != NULL) { unsigned int *t = (unsigned int *)p; unsigned int size = *--t; #ifdef MINIMALDEBUG if (size>=100000000) { CryError( "\001[CRYMANAGER ERROR](CryFree): illegal size 0x%X - block header was corrupted",size); } #endif size += sizeof(int); #ifdef GARBAGEMEMORY //FIXME: *disabling* memset caused random crash??? memset(t, 0xBA, size); #endif g_TotalAllocatedMemory -= size; g_GlobPageBucketAllocator.dealloc(t, size); } } else { // With profiler. if (p != NULL) { FUNCTION_PROFILER_FAST( g_System,PROFILE_SYSTEM,g_bProfilerEnabled ); unsigned int *t = (unsigned int *)p; unsigned int size = *--t; #ifdef MINIMALDEBUG if (size>=100000000) { CryError( "\001[CRYMANAGER ERROR](CryFree): illegal size 0x%X - block header was corrupted",size); } #endif size += sizeof(int); #ifdef GARBAGEMEMORY //FIXME: *disabling* memset caused random crash??? memset(t, 0xBA, size); #endif g_TotalAllocatedMemory -= size; g_GlobPageBucketAllocator.dealloc(t, size); } } } CRYMEMORYMANAGER_API void CryFreeSize(void *p, size_t size) { g_ScriptAllocatedMemory -= size; g_TotalAllocatedMemory -= size; if (!g_bProfilerEnabled) { if (p != NULL) { #ifdef MINIMALDEBUG if (size>=100000000) { CryError("[CRYMANAGER ERROR](CryFreeSize): illegal size 0x%X - block header was corrupted",size ); } #endif #ifdef GARBAGEMEMORY //FIXME: idem memset(p, 0xBB, size); #endif g_GlobPageBucketAllocator.dealloc(p, size); } } else { // With profiler. if (p != NULL) { FUNCTION_PROFILER_FAST( g_System,PROFILE_SYSTEM,g_bProfilerEnabled ); #ifdef MINIMALDEBUG if (size>=100000000) { CryError("[CRYMANAGER ERROR](CryFreeSize): illegal size 0x%X - block header was corrupted",size ); } #endif #ifdef GARBAGEMEMORY //FIXME: idem memset(p, 0xBB, size); #endif g_GlobPageBucketAllocator.dealloc(p, size); } } } CRYMEMORYMANAGER_API void *CryRealloc(void *memblock, size_t size) { if (!g_bProfilerEnabled) { // Without profiler. if(memblock==NULL) return CryMalloc(size); else { void *np = CryMalloc(size); size_t oldsize = ((int *)memblock)[-1]; memcpy(np, memblock, size>oldsize ? oldsize : size); CryFree(memblock); return np; } } else { // With Profiler. FUNCTION_PROFILER_FAST( g_System,PROFILE_SYSTEM,g_bProfilerEnabled ); if(memblock==NULL) return CryMalloc(size); else { void *np = CryMalloc(size); size_t oldsize = ((int *)memblock)[-1]; memcpy(np, memblock, size>oldsize ? oldsize : size); CryFree(memblock); return np; } } } CRYMEMORYMANAGER_API void *CryReallocSize(void *memblock, size_t oldsize, size_t size) { g_ScriptAllocatedMemory += size; // -old size done in CryFreeSize g_TotalAllocatedMemory += size; if (!g_bProfilerEnabled) { if(memblock==NULL) { return (char*)g_GlobPageBucketAllocator.alloc(size) + g_nPrecaution; } else { void *np = (char*)g_GlobPageBucketAllocator.alloc(size) + g_nPrecaution; memcpy(np, memblock, size>oldsize ? oldsize : size); CryFreeSize(memblock, oldsize); return np; } } else { FUNCTION_PROFILER_FAST( g_System,PROFILE_SYSTEM,g_bProfilerEnabled ); if(memblock==NULL) { return (char*)g_GlobPageBucketAllocator.alloc(size) + g_nPrecaution; } else { void *np = (char*)g_GlobPageBucketAllocator.alloc(size) + g_nPrecaution; memcpy(np, memblock, size>oldsize ? oldsize : size); CryFreeSize(memblock, oldsize); return np; } } } CRYMEMORYMANAGER_API void CryFlushAll() // releases/resets ALL memory... this is useful for restarting the game { /* InitPoolContext(g_pool); for(int i = 0; i