diff options
Diffstat (limited to 'Runtime/Allocator/MemoryManager.cpp')
-rw-r--r-- | Runtime/Allocator/MemoryManager.cpp | 1659 |
1 files changed, 1659 insertions, 0 deletions
diff --git a/Runtime/Allocator/MemoryManager.cpp b/Runtime/Allocator/MemoryManager.cpp new file mode 100644 index 0000000..11d98b4 --- /dev/null +++ b/Runtime/Allocator/MemoryManager.cpp @@ -0,0 +1,1659 @@ +#include "UnityPrefix.h" +#include "Runtime/Allocator/MemoryManager.h" +#include "Runtime/Utilities/MemoryUtilities.h" +#include "Runtime/Utilities/BitUtility.h" +#include "Runtime/Threads/AtomicOps.h" +#include "Runtime/Profiler/MemoryProfiler.h" + +// under new clang -fpermissive is no longer available, and it is quite strict about proper signatures of global new/delete +// eg throw() is reserved for nonthrow only +#define STRICTCPP_NEW_DELETE_SIGNATURES UNITY_OSX || UNITY_IPHONE +#if STRICTCPP_NEW_DELETE_SIGNATURES + #define THROWING_NEW_THROW throw(std::bad_alloc) +#else + #define THROWING_NEW_THROW throw() +#endif + + + +#if UNITY_PS3 +#include "PlatformDependent/PS3Player/Allocator/PS3DlmallocAllocator.h" +#endif + +#define STOMP_MEMORY !UNITY_PS3 && (!UNITY_RELEASE) + +#define kMemoryManagerOverhead ((sizeof(int) + kDefaultMemoryAlignment - 1) & ~(kDefaultMemoryAlignment-1)) + +#if UNITY_XENON + +#include "PlatformDependent/Xbox360/Source/XenonMemory.h" + +#define UNITY_LL_ALLOC(l, s, a) xenon::trackedMalloc(s, a) +#define UNITY_LL_REALLOC(l, p, s, a) xenon::trackedRealloc(p, s, a) +#define UNITY_LL_FREE(l, p) xenon::trackedFree(p) + +#elif UNITY_PS3 + +void* operator new (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); } +void* operator new [] (size_t size, size_t align) throw() { return GetMemoryManager().Allocate (size == 0 ? 4 : size, align, kMemNewDelete); } + +#include "PlatformDependent/PS3Player/Allocator/PS3Memory.h" + +#define UNITY_LL_ALLOC(l,s,a) PS3Memory::Alloc(l,s,a) +#define UNITY_LL_REALLOC(l,p,s, a) PS3Memory::Realloc(l,p,s,a) +#define UNITY_LL_FREE(l,p) PS3Memory::Free(l, p) + +#elif UNITY_ANDROID + +#define UNITY_LL_ALLOC(l,s,a) ::memalign(a, s) +#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s) +#define UNITY_LL_FREE(l,p) ::free(p) + +#else + +#define UNITY_LL_ALLOC(l,s,a) ::malloc(s) +#define UNITY_LL_REALLOC(l,p,s,a) ::realloc(p, s) +#define UNITY_LL_FREE(l,p) ::free(p) + +#endif + +void* operator new (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line) +{ +#if ENABLE_MEM_PROFILER + bool root_was_set = false; + if (set_root) root_was_set = push_allocation_root(NULL, false); +#endif + void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line); +#if ENABLE_MEM_PROFILER + if (root_was_set) pop_allocation_root(); + if (set_root) { + GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL); + push_allocation_root(p, true); + } +#endif + return p; +} +void* operator new [] (size_t size, MemLabelRef label, bool set_root, int align, const char* file, int line) +{ +#if ENABLE_MEM_PROFILER + bool root_was_set = false; + if (set_root) root_was_set = push_allocation_root(NULL, false); +#endif + void* p = malloc_internal (size, align, label, kAllocateOptionNone, file, line); +#if ENABLE_MEM_PROFILER + if (root_was_set) pop_allocation_root(); + if (set_root) { + GetMemoryProfiler()->RegisterRootAllocation(p, GetMemoryManager().GetAllocator(label), NULL, NULL); + push_allocation_root(p, true); + } +#endif + return p; +} +void operator delete (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); } +void operator delete [] (void* p, MemLabelRef label, bool /*set_root*/, int /*align*/, const char* /*file*/, int /*line*/) { free_alloc_internal (p, label); } + +#if ENABLE_MEMORY_MANAGER +#include "Runtime/Allocator/UnityDefaultAllocator.h" +#include "Runtime/Allocator/DynamicHeapAllocator.h" +#include "Runtime/Allocator/LowLevelDefaultAllocator.h" +#include "Runtime/Allocator/StackAllocator.h" +#include "Runtime/Allocator/TLSAllocator.h" +#include "Runtime/Allocator/DualThreadAllocator.h" +#include "Runtime/Threads/Thread.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Utilities/Word.h" +#include "Runtime/Threads/ThreadSpecificValue.h" + +#if UNITY_IPHONE + #include "PlatformDependent/iPhonePlayer/iPhoneNewLabelAllocator.h" +#endif + +#include <map> + +typedef DualThreadAllocator< DynamicHeapAllocator< LowLevelAllocator > > MainThreadAllocator; +typedef TLSAllocator< StackAllocator > TempTLSAllocator; + +static MemoryManager* g_MemoryManager = NULL; + +#if UNITY_FLASH + extern "C" void NativeExt_FreeMemManager(void* p){ + GetMemoryManager().Deallocate (p, kMemNewDelete); + } +#endif + +// new override does not work on mac together with pace +#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR) && !(UNITY_WEBGL) && !(UNITY_BB10) && !(UNITY_TIZEN) +void* operator new (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); } +void* operator new [] (size_t size) THROWING_NEW_THROW { return GetMemoryManager().Allocate (size==0?4:size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); } +void operator delete (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); } +void operator delete [] (void* p) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); } + +void* operator new (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New"); } +void* operator new [] (size_t size, const std::nothrow_t&) throw() { return GetMemoryManager().Allocate (size, kDefaultMemoryAlignment, kMemNewDelete, kAllocateOptionNone, "Overloaded New[]"); }; +void operator delete (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); } +void operator delete [] (void* p, const std::nothrow_t&) throw() { GetMemoryManager().Deallocate (p, kMemNewDelete); } +#endif + +#if UNITY_EDITOR +static size_t kDynamicHeapChunkSize = 16*1024*1024; +static size_t kTempAllocatorMainSize = 4*1024*1024; +static size_t kTempAllocatorThreadSize = 64*1024; +#elif UNITY_IPHONE || UNITY_ANDROID || UNITY_WII || UNITY_XENON || UNITY_PS3 || UNITY_FLASH || UNITY_WEBGL || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN +# if !UNITY_IPHONE && !UNITY_WP8 +static size_t kDynamicHeapChunkSize = 1*1024*1024; +# endif +static size_t kTempAllocatorMainSize = 128*1024; +static size_t kTempAllocatorThreadSize = 64*1024; +#else +// Win/osx/linux players +static size_t kDynamicHeapChunkSize = 4*1024*1024; +static size_t kTempAllocatorMainSize = 512*1024; +static size_t kTempAllocatorThreadSize = 64*1024; +#endif + +#if ENABLE_MEMORY_MANAGER + + +void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label); + +static int AlignUp(int value, int alignment) +{ + UInt32 ptr = value; + UInt32 bitMask = (alignment - 1); + UInt32 lowBits = ptr & bitMask; + UInt32 adjust = ((alignment - lowBits) & bitMask); + return adjust; +} + +void* GetPreallocatedMemory(int size) +{ + const size_t numAllocators = 20; // should be configured per platform + const size_t additionalStaticMem = sizeof(UnityDefaultAllocator<void>) * numAllocators; + + // preallocated memory for memorymanager and allocators + static const int preallocatedSize = sizeof(MemoryManager) + additionalStaticMem; +#if UNITY_PS3 + static char __attribute__((aligned(16))) g_MemoryBlockForMemoryManager[preallocatedSize]; +#elif UNITY_XENON + static char __declspec(align(16)) g_MemoryBlockForMemoryManager[preallocatedSize]; +#else + static char g_MemoryBlockForMemoryManager[preallocatedSize]; +#endif + static char* g_MemoryBlockPtr = g_MemoryBlockForMemoryManager; + + size += AlignUp(size, 16); + + void* ptr = g_MemoryBlockPtr; + g_MemoryBlockPtr+=size; + // Ensure that there is enough space on the preallocated block + if(g_MemoryBlockPtr > g_MemoryBlockForMemoryManager + preallocatedSize) + return NULL; + return ptr; +} +#endif + +#if UNITY_WIN && ENABLE_MEM_PROFILER +#define _CRTBLD +#include <..\crt\src\dbgint.h> +_CRT_ALLOC_HOOK pfnOldCrtAllocHook; +int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber); +#endif + +void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + return GetMemoryManager ().Allocate(size, align, label, allocateOptions, file, line); +} + +void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + void* ptr = GetMemoryManager ().Allocate(size*count, align, label, allocateOptions, file, line); + if (ptr) memset (ptr, 0, size*count); + return ptr; +} + +void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + return GetMemoryManager ().Reallocate(ptr, size, align, label, allocateOptions, file, line); +} + +void free_internal(void* ptr) +{ + // used for mac, since malloc is not hooked from the start, so a number of mallocs will have passed through + // before we wrap. This results in pointers being freed, that were not allocated with the memorymanager + // therefore we need the alloc->Contains check() + GetMemoryManager().Deallocate (ptr); +} + +void free_alloc_internal(void* ptr, MemLabelRef label) +{ + GetMemoryManager().Deallocate (ptr, label); +} + +#if (UNITY_OSX && UNITY_EDITOR) + +#include <malloc/malloc.h> +#include <mach/vm_map.h> +void *(*systemMalloc)(malloc_zone_t *zone, size_t size); +void *(*systemCalloc)(malloc_zone_t *zone, size_t num_items, size_t size); +void *(*systemValloc)(malloc_zone_t *zone, size_t size); +void *(*systemRealloc)(malloc_zone_t *zone, void* ptr, size_t size); +void *(*systemMemalign)(malloc_zone_t *zone, size_t align, size_t size); +void (*systemFree)(malloc_zone_t *zone, void *ptr); +void (*systemFreeSize)(malloc_zone_t *zone, void *ptr, size_t size); + +void* my_malloc(malloc_zone_t *zone, size_t size) +{ + void* ptr = (*systemMalloc)(zone,size); + MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr); + return ptr; + +} + +void* my_calloc(malloc_zone_t *zone, size_t num_items, size_t size) +{ + void* ptr = (*systemCalloc)(zone,num_items,size); + MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr); + return ptr; + +} + +void* my_valloc(malloc_zone_t *zone, size_t size) +{ + void* ptr = (*systemValloc)(zone,size); + MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr); + return ptr; +} + +void* my_realloc(malloc_zone_t *zone, void* ptr, size_t size) +{ + MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone, ptr); + void* newptr = (*systemRealloc)(zone,ptr,size); + MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, newptr); + return newptr; +} + +void* my_memalign(malloc_zone_t *zone, size_t align, size_t size) +{ + void* ptr = (*systemMemalign)(zone,align,size); + MemoryManager::m_LowLevelAllocated+=(*malloc_default_zone()->size)(zone, ptr); + return ptr; +} + +void my_free(malloc_zone_t *zone, void *ptr) +{ + int oldsize = (*malloc_default_zone()->size)(zone,ptr); + MemoryManager::m_LowLevelAllocated-=oldsize; + systemFree(zone,ptr); +} + +void my_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) +{ + MemoryManager::m_LowLevelAllocated-=(*malloc_default_zone()->size)(zone,ptr); + systemFreeSize(zone,ptr,size); +} + +#endif + +void InitializeMemory() +{ + InitializeMemoryLabels(); + +#if (UNITY_OSX && UNITY_EDITOR) + + UInt32 osxversion = 0; + Gestalt(gestaltSystemVersion, (MacSInt32 *) &osxversion); + + // overriding malloc_zone on osx 10.5 causes unity not to start up. + if(osxversion >= 0x01060) + { + malloc_zone_t* dz = malloc_default_zone(); + + systemMalloc = dz->malloc; + systemCalloc = dz->calloc; + systemValloc = dz->valloc; + systemRealloc = dz->realloc; + systemMemalign = dz->memalign; + systemFree = dz->free; + systemFreeSize = dz->free_definite_size; + + if(dz->version>=8) + vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ | VM_PROT_WRITE);//remove the write protection + + dz->malloc=&my_malloc; + dz->calloc=&my_calloc; + dz->valloc=&my_valloc; + dz->realloc=&my_realloc; + dz->memalign=&my_memalign; + dz->free=&my_free; + dz->free_definite_size=&my_free_definite_size; + + if(dz->version>=8) + vm_protect(mach_task_self(), (uintptr_t)dz, sizeof(malloc_zone_t), 0, VM_PROT_READ);//put the write protection back + + } +#endif +} + +MemoryManager& GetMemoryManager() +{ + if (g_MemoryManager == NULL){ + InitializeMemory(); + g_MemoryManager = HEAP_NEW(MemoryManager)(); + } + return *g_MemoryManager ; +} + +volatile long MemoryManager::m_LowLevelAllocated = 0; +volatile long MemoryManager::m_RegisteredGfxDriverMemory = 0; + +#if ENABLE_MEMORY_MANAGER + +#if _DEBUG || UNITY_EDITOR +UNITY_TLS_VALUE(bool) s_DisallowAllocationsOnThread; +inline void MemoryManager::CheckDisalowAllocation() +{ + DebugAssert(IsActive()); + + // Some codepaths in Unity disallow allocations. For example when a GC is running all threads are stopped. + // If we allowed allocations we would allow for very hard to find race conditions. + // Thus we explicitly check for it in debug builds. + if (s_DisallowAllocationsOnThread) + { + s_DisallowAllocationsOnThread = false; + FatalErrorMsg("CheckDisalowAllocation. Allocating memory when it is not allowed to allocate memory.\n"); + } +} + +#else +inline void MemoryManager::CheckDisalowAllocation() +{ +} +#endif + +MemoryManager::MemoryManager() +: m_NumAllocators(0) +, m_FrameTempAllocator(NULL) +, m_IsInitialized(false) +, m_IsActive(false) +{ +#if UNITY_WIN && ENABLE_MEM_PROFILER +// pfnOldCrtAllocHook = _CrtSetAllocHook(catchMemoryAllocHook); +#endif + + memset (m_Allocators, 0, sizeof(m_Allocators)); + memset (m_MainAllocators, 0, sizeof(m_MainAllocators)); + memset (m_ThreadAllocators, 0, sizeof(m_ThreadAllocators)); + memset (m_AllocatorMap, 0, sizeof(m_AllocatorMap)); + + // Main thread will not have a valid TLSAlloc until ThreadInitialize() is called! +#if UNITY_FLASH || UNITY_WEBGL + m_InitialFallbackAllocator = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>) ("ALLOC_FALLBACK"); +#else + m_InitialFallbackAllocator = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024, 0, true,"ALLOC_FALLBACK"); +#endif + + for (int i = 0; i < kMemLabelCount; i++) + m_AllocatorMap[i].alloc = m_InitialFallbackAllocator; +} + +void MemoryManager::StaticInitialize() +{ + GetMemoryManager().ThreadInitialize(); +} + +void MemoryManager::StaticDestroy() +{ +#if !UNITY_OSX + // not able to destroy profiler and memorymanager on osx because apple.coreaudio is still running a thread. + // FMOD is looking into shutting this down properly. Untill then, don't cleanup +#if ENABLE_MEM_PROFILER + MemoryProfiler::StaticDestroy(); +#endif + GetMemoryManager().ThreadCleanup(); +#endif +} + +void MemoryManager::InitializeMainThreadAllocators() +{ + m_FrameTempAllocator = HEAP_NEW(TempTLSAllocator)("ALLOC_TEMP_THREAD"); + +#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX + BaseAllocator* defaultThreadAllocator = NULL; + m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize, 1024, false,"ALLOC_DEFAULT_MAIN"); + m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,1024, true,"ALLOC_DEFAULT_THREAD"); + BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_DEFAULT", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]); + defaultThreadAllocator = m_ThreadAllocators[m_NumAllocators]; + m_NumAllocators++; +#else + BaseAllocator* defaultAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_DEFAULT"); +#endif + + for (int i = 0; i < kMemLabelCount; i++) + m_AllocatorMap[i].alloc = defaultAllocator; + + m_AllocatorMap[kMemTempAllocId].alloc = m_FrameTempAllocator; + m_AllocatorMap[kMemStaticStringId].alloc = m_InitialFallbackAllocator; + +#if UNITY_IPHONE + m_AllocatorMap[kMemNewDeleteId].alloc = m_Allocators[m_NumAllocators++] = HEAP_NEW(IphoneNewLabelAllocator); +#endif + +#if (UNITY_WIN && !UNITY_WP8) || UNITY_OSX + m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_GFX_MAIN"); + m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_GFX_THREAD"); + BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_GFX", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]); + BaseAllocator* gfxThreadAllocator = m_ThreadAllocators[m_NumAllocators]; + m_NumAllocators++; + + m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (8*1024*1024,0, false,"ALLOC_CACHEOBJECTS_MAIN"); + m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (2*1024*1024,0, true,"ALLOC_CACHEOBJECTS_THREAD"); + BaseAllocator* cacheAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_CACHEOBJECTS", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]); + m_NumAllocators++; + + m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (kDynamicHeapChunkSize,0, false,"ALLOC_TYPETREE_MAIN"); + m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (1024*1024,0, true,"ALLOC_TYPETREE_THREAD"); + BaseAllocator* typetreeAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_TYPETREE", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]); + m_NumAllocators++; + + m_MainAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, false,"ALLOC_PROFILER_MAIN"); + m_ThreadAllocators[m_NumAllocators] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>) (4*1024*1024,0, true,"ALLOC_PROFILER_THREAD"); + BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators] = HEAP_NEW(MainThreadAllocator)("ALLOC_PROFILER", m_MainAllocators[m_NumAllocators], m_ThreadAllocators[m_NumAllocators]); + m_NumAllocators++; + + m_AllocatorMap[kMemDynamicGeometryId].alloc + = m_AllocatorMap[kMemImmediateGeometryId].alloc + = m_AllocatorMap[kMemGeometryId].alloc + = m_AllocatorMap[kMemVertexDataId].alloc + = m_AllocatorMap[kMemBatchedGeometryId].alloc + = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator; + + m_AllocatorMap[kMemTypeTreeId].alloc = typetreeAllocator; + + m_AllocatorMap[kMemThreadId].alloc = defaultThreadAllocator; + m_AllocatorMap[kMemGfxThreadId].alloc = gfxThreadAllocator; + + m_AllocatorMap[kMemTextureCacheId].alloc = cacheAllocator; + m_AllocatorMap[kMemSerializationId].alloc = cacheAllocator; + m_AllocatorMap[kMemFileId].alloc = cacheAllocator; + + m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator; + m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator; + m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator; + +#elif UNITY_XENON +#if 1 + // DynamicHeapAllocator uses TLSF pools which are O(1) constant time for alloc/free + // It should be used for high alloc/dealloc traffic + BaseAllocator* dynAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocator>)(32*1024*1024, 0, true, "ALLOC_TINYBLOCKS"); + m_AllocatorMap[kMemBaseObjectId].alloc = dynAllocator; + m_AllocatorMap[kMemAnimationId].alloc = dynAllocator; + m_AllocatorMap[kMemSTLId].alloc = dynAllocator; + m_AllocatorMap[kMemNewDeleteId].alloc = dynAllocator; +#else + BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT"); + BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX"); + + BaseAllocator* profilerAllocator; +#if XBOX_USE_DEBUG_MEMORY + if (xenon::GetIsDebugMemoryEnabled()) + profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(DynamicHeapAllocator<LowLevelAllocatorDebugMem>)(16*1024*1024, 0, true, "ALLOC_PROFILER"); + else +#endif + profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER"); + + m_AllocatorMap[kMemDynamicGeometryId].alloc + = m_AllocatorMap[kMemImmediateGeometryId].alloc + = m_AllocatorMap[kMemGeometryId].alloc + = m_AllocatorMap[kMemVertexDataId].alloc + = m_AllocatorMap[kMemBatchedGeometryId].alloc + = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator; + + m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator; + + m_AllocatorMap[kMemProfilerId].alloc = profilerAllocator; + m_AllocatorMap[kMemMemoryProfilerId].alloc = profilerAllocator; + m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator; +#endif + +#elif UNITY_PS3 + + BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT"); + BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX"); + BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER"); + BaseAllocator* vertexDataAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(PS3DlmallocAllocator)("PS3_DLMALLOC_ALLOCATOR"))); + BaseAllocator* delayedReleaseAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DelayedReleaseAllocator("PS3_DELAYED_RELEASE_ALLOCATOR_PROXY", HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("PS3_DELAYED_RELEASE_ALLOCATOR"))); + BaseAllocator* ioMappedAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(PS3DlmallocAllocator("PS3_IOMAPPED_ALLOCATOR")); + + m_AllocatorMap[kMemDynamicGeometryId].alloc + = m_AllocatorMap[kMemImmediateGeometryId].alloc + = m_AllocatorMap[kMemGeometryId].alloc + = m_AllocatorMap[kMemVertexDataId].alloc + = m_AllocatorMap[kMemBatchedGeometryId].alloc + = m_AllocatorMap[kMemPS3RingBuffers.label].alloc + = m_AllocatorMap[kMemPS3RSXBuffers.label].alloc + = m_AllocatorMap[kMemTextureId].alloc = ioMappedAllocator; + + m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator; + m_AllocatorMap[kMemProfilerId].alloc = + m_AllocatorMap[kMemMemoryProfilerId].alloc = + m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator; + + m_AllocatorMap[kMemSkinningId].alloc = +// m_AllocatorMap[kMemSkinningTempId].alloc = + m_AllocatorMap[kMemPS3DelayedReleaseId].alloc = delayedReleaseAllocator; + + m_AllocatorMap[kMemVertexDataId].alloc = vertexDataAllocator; + +#else + + BaseAllocator* gameObjectAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GAMEOBJECT"); + BaseAllocator* gfxAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_GFX"); + BaseAllocator* profilerAllocator = m_Allocators[m_NumAllocators++] = HEAP_NEW(UnityDefaultAllocator<LowLevelAllocator>)("ALLOC_PROFILER"); + + m_AllocatorMap[kMemDynamicGeometryId].alloc + = m_AllocatorMap[kMemImmediateGeometryId].alloc + = m_AllocatorMap[kMemGeometryId].alloc + = m_AllocatorMap[kMemVertexDataId].alloc + = m_AllocatorMap[kMemBatchedGeometryId].alloc + = m_AllocatorMap[kMemTextureId].alloc = gfxAllocator; + + m_AllocatorMap[kMemBaseObjectId].alloc = gameObjectAllocator; + + m_AllocatorMap[kMemProfilerId].alloc + = m_AllocatorMap[kMemMemoryProfilerId].alloc + = m_AllocatorMap[kMemMemoryProfilerStringId].alloc = profilerAllocator; + +#endif + + m_IsInitialized = true; + m_IsActive = true; + +#if ENABLE_MEM_PROFILER + MemoryProfiler::StaticInitialize(); +#endif + + Assert(m_FrameTempAllocator); +} + +MemoryManager::~MemoryManager() +{ + for(int i = 0; i < m_NumAllocators; i++) + { + Assert(m_Allocators[i]->GetAllocatedMemorySize() == 0); + } + + ThreadCleanup(); + + for (int i = 0; i < m_NumAllocators; i++){ + HEAP_DELETE (m_Allocators[i], BaseAllocator); + } +#if UNITY_WIN +#if ENABLE_MEM_PROFILER +// _CrtSetAllocHook(pfnOldCrtAllocHook); +#endif +#endif +} + +class MemoryManagerAutoDestructor +{ +public: + MemoryManagerAutoDestructor(){} + ~MemoryManagerAutoDestructor() + { + //HEAP_DELETE(g_MemoryManager, MemoryManager); + } +}; + +MemoryManagerAutoDestructor g_MemoryManagerAutoDestructor; + + +#else + + +MemoryManager::MemoryManager() +: m_NumAllocators(0) +, m_FrameTempAllocator(NULL) +{ +} + +MemoryManager::~MemoryManager() +{ +} + +#endif + +#if UNITY_WIN +#include <winnt.h> +#define ON_WIN(x) x +#else +#define ON_WIN(x) +#endif + +#if ENABLE_MEMORY_MANAGER + +void* MemoryManager::LowLevelAllocate(size_t size) +{ + ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) ); + int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, size + kMemoryManagerOverhead, kDefaultMemoryAlignment); + if(ptr != NULL) + { + *ptr = size; + ptr += (kMemoryManagerOverhead >> 2); + } + + return ptr; +} + +void* MemoryManager::LowLevelCAllocate(size_t count, size_t size) +{ + ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, count*size) ); + int allocSize = count*size + kMemoryManagerOverhead; + int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault, allocSize, kDefaultMemoryAlignment); + if(ptr != NULL) + { + memset(ptr, 0, allocSize); + *ptr = count*size; + ptr += (kMemoryManagerOverhead >> 2); + } + return ptr; +} + +void* MemoryManager::LowLevelReallocate( void* p, size_t size ) +{ + int* ptr = (int*) p; + ptr -= kMemoryManagerOverhead >> 2; + ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) ); + int* newptr = (int*)UNITY_LL_REALLOC(kMemDefault, ptr, size + kMemoryManagerOverhead, kDefaultMemoryAlignment); + if(newptr != NULL) + { + *newptr = size; + newptr += (kMemoryManagerOverhead >> 2); + } + ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, size) ); + return newptr; +} + +void MemoryManager::LowLevelFree(void* p){ + if(p == NULL) + return; + int* ptr = (int*) p; + ptr -= kMemoryManagerOverhead >> 2; + ON_WIN( InterlockedExchangeAdd(&m_LowLevelAllocated, -*ptr) ); + UNITY_LL_FREE(kMemDefault,ptr); +} + +void MemoryManager::ThreadInitialize(size_t tempSize) +{ + int tempAllocatorSize = kTempAllocatorThreadSize; + if(Thread::CurrentThreadIsMainThread() && !m_IsInitialized) + { + InitializeMainThreadAllocators(); + tempAllocatorSize = kTempAllocatorMainSize; + } + + if(tempSize != 0) + tempAllocatorSize = tempSize; + + StackAllocator* tempAllocator = UNITY_NEW(StackAllocator(tempAllocatorSize, "ALLOC_TEMP_THREAD"), kMemManager); + m_FrameTempAllocator->ThreadInitialize(tempAllocator); +} + +void MemoryManager::ThreadCleanup() +{ + for(int i = 0; i < m_NumAllocators; i++) + m_Allocators[i]->ThreadCleanup(); + + if(Thread::CurrentThreadIsMainThread()) + { + m_FrameTempAllocator->ThreadCleanup(); + m_FrameTempAllocator = NULL; + + m_IsActive = false; + +#if !UNITY_EDITOR + for(int i = 0; i < m_NumAllocators; i++) + { + HEAP_DELETE(m_Allocators[i], BaseAllocator); + if(m_MainAllocators[i]) + HEAP_DELETE(m_MainAllocators[i], BaseAllocator); + if(m_ThreadAllocators[i]) + HEAP_DELETE(m_ThreadAllocators[i], BaseAllocator); + m_Allocators[i] = 0; + m_MainAllocators[i] = 0; + m_ThreadAllocators[i] = 0; + } + m_NumAllocators = 0; +#else + for(int i = 0; i < m_NumAllocators; i++) + { + if(m_MainAllocators[i]) + m_Allocators[i] = m_MainAllocators[i]; + } +#endif + for (int i = 0; i < kMemLabelCount; i++) + m_AllocatorMap[i].alloc = m_InitialFallbackAllocator; + + return; + } +#if ENABLE_MEM_PROFILER + GetMemoryProfiler()->ThreadCleanup(); +#endif + m_FrameTempAllocator->ThreadCleanup(); +} + +void MemoryManager::FrameMaintenance(bool cleanup) +{ + m_FrameTempAllocator->FrameMaintenance(cleanup); + for(int i = 0; i < m_NumAllocators; i++) + m_Allocators[i]->FrameMaintenance(cleanup); +} + +#else + +void* MemoryManager::LowLevelAllocate(int size) +{ + return UNITY_LL_ALLOC(kMemDefault,size+sizeof(int), 4); +} + +void* MemoryManager::LowLevelCAllocate(int count, int size) +{ + int* ptr = (int*)UNITY_LL_ALLOC(kMemDefault,count*size, 4); + memset(ptr,0,count*size); + return ptr; +} + +void* MemoryManager::LowLevelReallocate( void* p, int size ) +{ + return UNITY_LL_REALLOC(kMemDefault,p, size,4); +} + +void MemoryManager::LowLevelFree(void* p){ + UNITY_LL_FREE(kMemDefault,p); +} + +void MemoryManager::ThreadInitialize(size_t tempSize) +{} + +void MemoryManager::ThreadCleanup() +{} + +#endif + +void OutOfMemoryError( size_t size, int align, MemLabelRef label, int line, const char* file ) +{ + TEMP_STRING str; + str.reserve(30*1024); + str += FormatString<TEMP_STRING>("Could not allocate memory: System out of memory!\n"); + str += FormatString<TEMP_STRING>("Trying to allocate: %" PRINTF_SIZET_FORMAT "B with %d alignment. MemoryLabel: %s\n", size, align, GetMemoryManager().GetMemcatName(label)); + str += FormatString<TEMP_STRING>("Allocation happend at: Line:%d in %s\n", line, file); + PrintShortMemoryStats(str, label); + // first call the plain printf_console, to make a printout that doesn't do callstack and other allocations + printf_console("%s", str.c_str()); + // Then do a FatalErroString, that brings up a dialog and launches the bugreporter. + FatalErrorString(str.c_str()); +} + +inline void* CheckAllocation(void* ptr, size_t size, int align, MemLabelRef label, const char* file, int line) +{ + if(ptr == NULL) + OutOfMemoryError(size, align, label, line, file); + return ptr; +} + +void MemoryManager::DisallowAllocationsOnThisThread() +{ +#if _DEBUG || UNITY_EDITOR + s_DisallowAllocationsOnThread = true; +#endif +} +void MemoryManager::ReallowAllocationsOnThisThread() +{ +#if _DEBUG || UNITY_EDITOR + s_DisallowAllocationsOnThread = false; +#endif +} + +void* MemoryManager::Allocate(size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */) +{ + DebugAssert(IsPowerOfTwo(align)); + DebugAssert(align != 0); + + align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment) + + // Fallback to backup allocator if we have not yet initialized the MemoryManager + if(!IsActive()) + { + void* ptr = m_InitialFallbackAllocator->Allocate(size, align); + #if ENABLE_MEM_PROFILER + if (ptr) + MemoryProfiler::InitAllocation(ptr, m_InitialFallbackAllocator); + #endif + return ptr; + } + + if (IsTempAllocatorLabel(label)) + { + void* ptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Allocate(size, align); + if(ptr) + return ptr; + // if tempallocator thread has not been initialized fallback to defualt + return Allocate(size, align, kMemDefault, allocateOptions, file, line); + } + + BaseAllocator* alloc = GetAllocator(label); + CheckDisalowAllocation(); + + void* ptr = alloc->Allocate(size, align); + + if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !ptr) + return NULL; + + CheckAllocation( ptr, size, align, label, file, line ); + + #if ENABLE_MEM_PROFILER + RegisterAllocation(ptr, size, alloc, label, "Allocate", file, line); + #endif + + DebugAssert(((int)ptr & (align-1)) == 0); + +#if STOMP_MEMORY + memset(ptr, 0xcd, size); +#endif + + return ptr; +} + +void* MemoryManager::Reallocate(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions/* = kNone*/, const char* file /* = NULL */, int line /* = 0 */) +{ + DebugAssert(IsPowerOfTwo(align)); + DebugAssert(align != 0); + + if(ptr == NULL) + return Allocate(size,align,label,allocateOptions,file,line); + + align = ((align-1) | (kDefaultMemoryAlignment-1)) + 1; // Max(align, kDefaultMemoryAlignment) + + // Fallback to backup allocator if we have not yet initialized the MemoryManager + if(!IsActive()) + return m_InitialFallbackAllocator->Reallocate(ptr, size, align); + + if (IsTempAllocatorLabel(label)) + { + void* newptr = ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::Reallocate(ptr, size, align); + if(newptr) + return newptr; + // if tempallocator thread has not been initialized fallback to defualt + return Reallocate( ptr, size, align, kMemDefault, allocateOptions, file, line); + } + + BaseAllocator* alloc = GetAllocator(label); + CheckDisalowAllocation(); + + if(ptr != NULL && !alloc->Contains(ptr)) + { + // It wasn't the expected allocator that contained the pointer. + // allocate on the expected allocator and move the memory there + void* newptr = Allocate(size,align,label,allocateOptions,file,line); + if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr) + return NULL; + + int oldSize = GetAllocatorContainingPtr(ptr)->GetPtrSize(ptr); + memcpy(newptr, ptr, size<oldSize?size:oldSize); + + Deallocate(ptr); + return newptr; + } + + #if ENABLE_MEM_PROFILER + // register the deletion of the old allocation and extract the old root owner into the label + ProfilerAllocationHeader* root = RegisterDeallocation(ptr, alloc, label, "Reallocate"); + #endif + + void* newptr = alloc->Reallocate(ptr, size, align); + + if ((allocateOptions & kAllocateOptionReturnNullIfOutOfMemory) && !newptr) + return NULL; + + CheckAllocation( newptr, size, align, label, file, line ); + + #if ENABLE_MEM_PROFILER + RegisterAllocation(newptr, size, alloc, MemLabelId(label.label, root), "Reallocate", file, line); + #endif + + DebugAssert(((int)newptr & (align-1)) == 0); + + return newptr; +} + +void MemoryManager::Deallocate(void* ptr, MemLabelRef label) +{ + if(ptr == NULL) + return; + + if(!IsActive()) // if we are outside the scope of Initialize/Destroy - fallback + return Deallocate(ptr); + + if (IsTempAllocatorLabel(label)) + { + if ( ((TempTLSAllocator*)m_FrameTempAllocator)->TempTLSAllocator::TryDeallocate(ptr) ) + return; + // If not found, Fallback to do a search for what allocator has the pointer + return Deallocate(ptr); + } + + BaseAllocator* alloc = GetAllocator(label); + CheckDisalowAllocation(); + + if(!alloc->Contains(ptr)) + return Deallocate(ptr); + +#if ENABLE_MEM_PROFILER + RegisterDeallocation(ptr, alloc, label, "Deallocate"); +#endif + +#if STOMP_MEMORY + memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr)); +#endif + + alloc->Deallocate(ptr); +} + +void MemoryManager::Deallocate(void* ptr) +{ + if (ptr == NULL) + return; + + BaseAllocator* alloc = GetAllocatorContainingPtr(ptr); + + if (alloc) + { + Assert (alloc != m_FrameTempAllocator); +#if ENABLE_MEM_PROFILER + if(GetMemoryProfiler() && alloc != m_InitialFallbackAllocator) + { + size_t oldsize = alloc->GetPtrSize(ptr); + GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, NULL, kMemDefault); + //RegisterDeallocation(MemLabelId(labelid), oldsize); // since we don't have the label, we will not register this deallocation + if(m_LogAllocations) + printf_console("Deallocate (%8X): %11d\tTotal: %.2fMB (%d)\n", (unsigned int)ptr, -(int)oldsize, GetTotalAllocatedMemory() / (1024.0f * 1024.0f), (int)GetTotalAllocatedMemory()); + } +#endif +#if STOMP_MEMORY + memset32(ptr, 0xdeadbeef, alloc->GetPtrSize(ptr)); +#endif + alloc->Deallocate(ptr); + } + else + { + if(IsActive()) + UNITY_LL_FREE (kMemDefault, ptr); + //else ignore the deallocation, because the allocator is no longer around + } +} + +struct ExternalAllocInfo +{ + size_t size; + size_t relatedID; + const char* file; + int line; +}; +typedef UNITY_MAP(kMemMemoryProfiler,void*,ExternalAllocInfo ) ExternalAllocationMap; +static ExternalAllocationMap* g_ExternalAllocations = NULL; +Mutex g_ExternalAllocationLock; +void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line) +{ + Mutex::AutoLock autolock(g_ExternalAllocationLock); + if (g_ExternalAllocations == NULL) + { + SET_ALLOC_OWNER(NULL); + g_ExternalAllocations = new ExternalAllocationMap(); + } + ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr); + if (it != g_ExternalAllocations->end()) + { + ErrorStringMsg("allocation 0x%p already registered @ %s:l%d size %d; now calling from %s:l%d size %d?", + ptr, + it->second.file, it->second.line, it->second.size, + file, line, size); + } + + if (related == 0) + related = (size_t)ptr; + + ExternalAllocInfo info; + info.size = size; + info.relatedID = related; + info.file = file; + info.line = line; + g_ExternalAllocations->insert(std::make_pair(ptr, info)); + MemoryManager::m_RegisteredGfxDriverMemory += size; +#if ENABLE_MEM_PROFILER + GetMemoryProfiler()->RegisterMemoryToID(related, size); +#endif +} + +void register_external_gfx_deallocation(void* ptr, const char* file, int line) +{ + if(ptr == NULL) + return; + Mutex::AutoLock autolock(g_ExternalAllocationLock); + if (g_ExternalAllocations == NULL) + g_ExternalAllocations = new ExternalAllocationMap(); + + ExternalAllocationMap::iterator it = g_ExternalAllocations->find(ptr); + if (it == g_ExternalAllocations->end()) + { + // not registered + return; + } + size_t size = it->second.size; + size_t related = it->second.relatedID; + MemoryManager::m_RegisteredGfxDriverMemory -= size; + g_ExternalAllocations->erase(it); +#if ENABLE_MEM_PROFILER + GetMemoryProfiler()->UnregisterMemoryToID(related,size); +#endif +} + + +typedef UNITY_MAP(kMemDefault,MemLabelIdentifier, BaseAllocator*) CustomAllocators; +CustomAllocators* g_CustomAllocators = NULL; +int nextCustomAllocatorIndex = (MemLabelIdentifier)0x1000; + +BaseAllocator* MemoryManager::GetAllocatorContainingPtr(const void* ptr) +{ + if(m_FrameTempAllocator && m_FrameTempAllocator->Contains(ptr)) + return m_FrameTempAllocator; + + for(int i = 0; i < m_NumAllocators ; i++) + { + if(m_Allocators[i]->IsAssigned() && m_Allocators[i]->Contains(ptr)) + return m_Allocators[i]; + } + + if(m_InitialFallbackAllocator->Contains(ptr)) + return m_InitialFallbackAllocator; + + if(g_CustomAllocators) + { + CustomAllocators::iterator it = g_CustomAllocators->begin(); + for(;it != g_CustomAllocators->end(); ++it) + if(it->second->Contains(ptr)) + return it->second; + } + return NULL; +} + +const char* MemoryManager::GetAllocatorName( int i ) +{ + return i < m_NumAllocators ? m_Allocators[i]->GetName() : "Custom"; +} + +const char* MemoryManager::GetMemcatName( MemLabelRef label ) +{ + return label.label < kMemLabelCount ? MemLabelName[label.label] : "Custom"; +} + +MemLabelId MemoryManager::AddCustomAllocator(BaseAllocator* allocator) +{ + Assert(allocator->Contains(NULL) == false); // to assure that Contains does not just return true + MemLabelIdentifier label =(MemLabelIdentifier)(nextCustomAllocatorIndex++); + if(!g_CustomAllocators) + g_CustomAllocators = UNITY_NEW(CustomAllocators,kMemDefault); + (*g_CustomAllocators)[label] = allocator; + return MemLabelId(label, NULL); +} + +void MemoryManager::RemoveCustomAllocator(BaseAllocator* allocator) +{ + if(!g_CustomAllocators) + return; + + for(CustomAllocators::iterator it = g_CustomAllocators->begin(); + it != g_CustomAllocators->end(); + ++it) + { + if (it->second == allocator) + { + g_CustomAllocators->erase(it); + break; + } + } + if(g_CustomAllocators->empty()) + UNITY_DELETE(g_CustomAllocators, kMemDefault); +} + +BaseAllocator* MemoryManager::GetAllocatorAtIndex( int index ) +{ + return m_Allocators[index]; +} + +BaseAllocator* MemoryManager::GetAllocator( MemLabelRef label ) +{ + DebugAssert(!IsTempAllocatorLabel(label)); + if(label.label < kMemLabelCount) + { + BaseAllocator* alloc = m_AllocatorMap[label.label].alloc; + return alloc; + } + else if(label.label == kMemLabelCount) + return NULL; + + if(g_CustomAllocators) + { + CustomAllocators::iterator it = g_CustomAllocators->find(label.label); + if(it != g_CustomAllocators->end()) + return (*it).second; + } + + return NULL; +} + +int MemoryManager::GetAllocatorIndex( BaseAllocator* alloc ) +{ + for(int i = 0; i < m_NumAllocators; i++) + if(alloc == m_Allocators[i]) + return i; + + return m_NumAllocators; +} + +size_t MemoryManager::GetTotalAllocatedMemory() +{ + size_t total = m_FrameTempAllocator->GetAllocatedMemorySize(); + for(int i = 0; i < m_NumAllocators ; i++) + total += m_Allocators[i]->GetAllocatedMemorySize(); + return total; +} + +size_t MemoryManager::GetTotalReservedMemory() +{ + size_t total = m_FrameTempAllocator->GetReservedSizeTotal(); + for(int i = 0; i < m_NumAllocators ; i++) + total += m_Allocators[i]->GetReservedSizeTotal(); + return total; +} + +size_t MemoryManager::GetTotalUnusedReservedMemory() +{ + return GetTotalReservedMemory() - GetTotalAllocatedMemory();; +} + +int MemoryManager::GetAllocatorCount( ) +{ + return m_NumAllocators; +} + +size_t MemoryManager::GetAllocatedMemory( MemLabelRef label ) +{ + return m_AllocatorMap[label.label].allocatedMemory; +} + +size_t MemoryManager::GetTotalProfilerMemory() +{ + return GetAllocator(kMemProfiler)->GetAllocatedMemorySize(); +} + +int MemoryManager::GetAllocCount( MemLabelRef label ) +{ + return m_AllocatorMap[label.label].numAllocs; +} + +size_t MemoryManager::GetLargestAlloc( MemLabelRef label ) +{ + return m_AllocatorMap[label.label].largestAlloc; +} + +void MemoryManager::StartLoggingAllocations(size_t logAllocationsThreshold) +{ + m_LogAllocations = true; + m_LogAllocationsThreshold = logAllocationsThreshold; +}; + +void MemoryManager::StopLoggingAllocations() +{ + m_LogAllocations = false; +}; + +#if ENABLE_MEM_PROFILER +void MemoryManager::RegisterAllocation(void* ptr, size_t size, BaseAllocator* alloc, MemLabelRef label, const char* function, const char* file, int line) +{ + if (GetMemoryProfiler()) + { + DebugAssert(!IsTempAllocatorLabel(label)); + if(label.label < kMemLabelCount) + { + m_AllocatorMap[label.label].allocatedMemory += size; + m_AllocatorMap[label.label].numAllocs++; + m_AllocatorMap[label.label].largestAlloc = std::max(m_AllocatorMap[label.label].largestAlloc, size); + } + GetMemoryProfiler()->RegisterAllocation(ptr, label, file, line, size); + if (m_LogAllocations && size >= m_LogAllocationsThreshold) + { + size_t totalAllocatedMemoryAfterAllocation = GetTotalAllocatedMemory(); + printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ") in %s:%d\n", + function, ptr, size, totalAllocatedMemoryAfterAllocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterAllocation, file, line + ); + } + } +} + +ProfilerAllocationHeader* MemoryManager::RegisterDeallocation(void* ptr, BaseAllocator* alloc, MemLabelRef label, const char* function) +{ + ProfilerAllocationHeader* relatedHeader = NULL; + if (ptr != NULL && GetMemoryProfiler()) + { + DebugAssert(!IsTempAllocatorLabel(label)); + size_t oldsize = alloc->GetPtrSize(ptr); + GetMemoryProfiler()->UnregisterAllocation(ptr, alloc, oldsize, &relatedHeader, label); + if(label.label < kMemLabelCount) + { + m_AllocatorMap[label.label].allocatedMemory -= oldsize; + m_AllocatorMap[label.label].numAllocs--; + } + + if (m_LogAllocations && oldsize >= m_LogAllocationsThreshold) + { + size_t totalAllocatedMemoryAfterDeallocation = GetTotalAllocatedMemory() - oldsize; + printf_console( "%s (%p): %11" PRINTF_SIZET_FORMAT "\tTotal: %.2fMB (%" PRINTF_SIZET_FORMAT ")\n", + function, ptr, -(int)oldsize, totalAllocatedMemoryAfterDeallocation / (1024.0f * 1024.0f), totalAllocatedMemoryAfterDeallocation); + } + } + return relatedHeader; +} +#endif + +void PrintShortMemoryStats(TEMP_STRING& str, MemLabelRef label) +{ +#if ENABLE_MEMORY_MANAGER + MemoryManager& mm = GetMemoryManager(); + str += "Memory overview\n\n"; + for(int i = 0; i < mm.GetAllocatorCount() ; i++) + { + BaseAllocator* alloc = mm.GetAllocatorAtIndex(i); + if(alloc == NULL) + continue; + str += FormatString<TEMP_STRING>( "\n[ %s ] used: %" PRINTF_SIZET_FORMAT "B | peak: %" PRINTF_SIZET_FORMAT "B | reserved: %" PRINTF_SIZET_FORMAT "B \n", + alloc->GetName(), alloc->GetAllocatedMemorySize(), alloc->GetPeakAllocatedMemorySize(), alloc->GetReservedSizeTotal()); + } +#endif +} + +#if UNITY_WIN && ENABLE_MEM_PROFILER + +// this is only here to catch stray mallocs - UNITY_MALLOC should be used instead +// use tls to mark if we are in our own malloc or not. register stray mallocs. +int catchMemoryAllocHook(int allocType, void *userData, size_t size, int blockType, long requestNumber, const unsigned char *filename, int lineNumber) +{ + if(!GetMemoryProfiler()) + return TRUE; + + if (allocType == _HOOK_FREE) + { +#ifdef _DEBUG + int headersize = sizeof(_CrtMemBlockHeader); + _CrtMemBlockHeader* header = (_CrtMemBlockHeader*)((size_t) userData - headersize); + GetMemoryProfiler()->UnregisterAllocation(NULL, NULL, header->nDataSize, NULL, kMemDefault); +#endif + } + else + GetMemoryProfiler()->RegisterAllocation(NULL, MemLabelId(kMemLabelCount, NULL), NULL, 0, size); + return TRUE; +} + +#endif + + + +#if ENABLE_UNIT_TESTS + +#include "External/UnitTest++/src/UnitTest++.h" + +SUITE (AtomicOpsTests) +{ + struct AtomicOpsFixture + { + AtomicOpsFixture() + { + } + ~AtomicOpsFixture() + { + } + }; + + TEST_FIXTURE(AtomicOpsFixture, AtomicExchange) + { +#if UNITY_WIN || UNITY_XENON || UNITY_ANDROID + int i = 2; + CHECK_EQUAL( 2, AtomicExchange( &i, 3 ) ); +#endif // UNITY_WIN || UNITY_XENON || UNITY_ANDROID + } + + TEST_FIXTURE(AtomicOpsFixture, AtomicCompareExchange) + { + int i = 1; + CHECK_EQUAL( true, AtomicCompareExchange( &i, 2, 1 ) ); + } + + TEST_FIXTURE(AtomicOpsFixture, AtomicDecrement) + { + int i = 2; + CHECK_EQUAL( 1, AtomicDecrement( &i ) ); + } + + TEST_FIXTURE(AtomicOpsFixture, AtomicIncrement) + { + int i = 0; + CHECK_EQUAL( 1, AtomicIncrement( &i ) ); + } + + TEST_FIXTURE(AtomicOpsFixture, AtomicSub) + { + int i = 2; + CHECK_EQUAL( 1, AtomicSub( &i, 1 ) ); + } + + TEST_FIXTURE(AtomicOpsFixture, AtomicAdd) + { + int i = 1; + CHECK_EQUAL( 2, AtomicAdd( &i, 1 ) ); + } +} + +#if ENABLE_MEMORY_MANAGER +#include "Runtime/Profiler/TimeHelper.h" +SUITE (MemoryManagerTests) +{ + struct MemoryManagerFixture + { + MemoryManagerFixture() + { + } + ~MemoryManagerFixture() + { + } + }; + + + struct VirtualTestStructA{ + VirtualTestStructA() {ptrA = UNITY_MALLOC(kMemDefault,1024*1024);} + virtual ~VirtualTestStructA() {UNITY_FREE(kMemDefault,ptrA);} + void* ptrA; + }; + struct VirtualTestStructB : public VirtualTestStructA{ + VirtualTestStructB() {ptrB = UNITY_MALLOC(kMemDefault,1024*1024);} + virtual ~VirtualTestStructB() {UNITY_FREE(kMemDefault,ptrB);} + void* ptrB; + }; + + + struct TestStruct{ + TestStruct() {ptr = UNITY_MALLOC(kMemDefault,1024*1024);} + ~TestStruct(){UNITY_FREE(kMemDefault,ptr);} + void* ptr; + }; + + TEST_FIXTURE(MemoryManagerFixture, NewDelete) + { + size_t memoryBefore = GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize(); + + TestStruct* test = UNITY_NEW(TestStruct, kMemDefault); + // less or equal, since some platforms have larger default alignment that sizeof(void*) + CHECK(memoryBefore + sizeof(TestStruct) + 1024*1024 <= GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize()); + UNITY_DELETE (test, kMemDefault); + + CHECK_EQUAL(memoryBefore,GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize()); + + VirtualTestStructA* testA = (VirtualTestStructA*)UNITY_NEW(VirtualTestStructB, kMemDefault); + UNITY_DELETE (testA, kMemDefault); + + CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize()); + + /* This will fail the tests because of theFatalError in out of mem - but good for testing the OOM message + void* outofmem = UNITY_MALLOC(kMemDefault,((size_t)-1) - 1024); // has to be room for headers etc + CHECK_EQUAL((int)outofmem, 0); + CHECK_EQUAL(memoryBefore, GetMemoryManager().GetAllocator(kMemDefault)->GetAllocatedMemorySize()); + UNITY_FREE(kMemDefault, outofmem);*/ + } + + TEST_FIXTURE(MemoryManagerFixture, CanAllocate) + { + UnityDefaultAllocator<LowLevelAllocator>* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc"); + MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc); + void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel); + + int requestedSize = testAlloc->GetAllocatedMemorySize(); + int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize; + int overheadSize = testAlloc->GetOverheadSize(ptr); + + CHECK_EQUAL(1024, requestedSize); + int page4size = 1<<(8-StaticLog2<kDefaultMemoryAlignment>::value); + CHECK_EQUAL(overheadSize + (128+1 + 128+1 + 32+1 + page4size+1)*sizeof(int*), overhead); + + GetMemoryManager().Deallocate(ptr); + + requestedSize = testAlloc->GetAllocatedMemorySize(); + overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize; + + CHECK_EQUAL(0, requestedSize); + CHECK_EQUAL(0, overhead); + + GetMemoryManager().RemoveCustomAllocator(testAlloc); + } + + TEST_FIXTURE(MemoryManagerFixture, CanAllocateAligned) + { + BaseAllocator* testAlloc = new UnityDefaultAllocator<LowLevelAllocator>("TestAlloc"); + MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc); + for(int i = 0; i < 100; i++) + { + int size = 1024 + ((i*20457)&1023); + int align = 1<<(1+((i*3)&7)); + void* ptr = GetMemoryManager().Allocate(size, align, testAllocLabel); + ((int*)ptr)[0] = 0x89ABCDEF; + int requestedSize = testAlloc->GetAllocatedMemorySize(); + + CHECK_EQUAL(size, requestedSize); + CHECK_EQUAL(0, ((int)ptr)&(align-1)); + + int newsize = 1024 + ((i*236047)&1023); + ptr = GetMemoryManager().Reallocate(ptr, newsize, align, testAllocLabel); + + requestedSize = testAlloc->GetAllocatedMemorySize(); + + CHECK_EQUAL(0x89ABCDEF, ((int*)ptr)[0]); + CHECK_EQUAL(newsize, requestedSize); + CHECK_EQUAL(0, ((int)ptr)&(align-1)); + + GetMemoryManager().Deallocate(ptr); + + requestedSize = testAlloc->GetAllocatedMemorySize(); + + CHECK_EQUAL(0, requestedSize); + } + int requestedSize = testAlloc->GetAllocatedMemorySize(); + int overhead = testAlloc->GetAllocatorSizeTotalUsed() - requestedSize; + + CHECK_EQUAL(0, requestedSize); + CHECK_EQUAL(0, overhead); + + GetMemoryManager().RemoveCustomAllocator(testAlloc); + } + + TEST_FIXTURE(MemoryManagerFixture, CanTempAllocate) + { + GetMemoryManager().FrameMaintenance(); + void* fillptr = UNITY_MALLOC(kMemTempAlloc, 128); + for(int i = 0; i < 1000; i++) + { + void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, 128, 16); + UNITY_FREE(kMemTempAlloc, ptr); + } + + void** ptrArray = (void**)UNITY_MALLOC(kMemTempAlloc,256*sizeof(void*)); + for(int i = 0; i < 256; i++) + { + ptrArray[i] = UNITY_MALLOC_ALIGNED(kMemTempAlloc,16*1024,32); + } + + for(int i = 0; i < 256; i++) + { + UNITY_FREE(kMemTempAlloc, ptrArray[i]); + } + UNITY_FREE(kMemTempAlloc, fillptr); + UNITY_FREE(kMemTempAlloc, ptrArray); + GetMemoryManager().FrameMaintenance(); + } + + + #if !UNITY_64 //@TODO: seems to use TLSF which is disabled in 64 bit? + TEST_FIXTURE(MemoryManagerFixture, DynamicHeapReallocate) + { + DynamicHeapAllocator<LowLevelAllocator>* testAlloc = new DynamicHeapAllocator<LowLevelAllocator>(100*1024,0,true,"TestAlloc"); + MemLabelId testAllocLabel = GetMemoryManager().AddCustomAllocator(testAlloc); + void* ptr = GetMemoryManager().Allocate(1024, 1, testAllocLabel); + + CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal()); + CHECK_EQUAL(1024, testAlloc->GetAllocatedMemorySize()); + + void* ptr2 = UNITY_MALLOC( testAllocLabel, 50*1024 ); + CHECK_EQUAL(100*1024, testAlloc->GetReservedSizeTotal()); + memset(ptr2,0x3B,50*1024); + void* ptr3 = UNITY_REALLOC_( testAllocLabel, ptr2, 100*1024 ); + CHECK_EQUAL(200*1024, testAlloc->GetReservedSizeTotal()); + for(int i = 0; i < 1024;i++) + CHECK_EQUAL(0x3B,((char*)ptr3)[i]); + for(int i = 49*1024; i < 50*1024;i++) + CHECK_EQUAL(0x3B,((char*)ptr3)[i]); + memset(ptr3,0x4C,100*1024); + void* ptr4 = UNITY_REALLOC_( testAllocLabel, ptr3, 101*1024 ); + CHECK_EQUAL(201*1024, testAlloc->GetReservedSizeTotal()); + for(int i = 0; i < 1024;i++) + CHECK_EQUAL(0x4C,((char*)ptr4)[i]); + for(int i = 99*1024; i < 100*1024;i++) + CHECK_EQUAL(0x4C,((char*)ptr4)[i]); + + CHECK_EQUAL(1024 + 101*1024, testAlloc->GetAllocatedMemorySize()); + + GetMemoryManager().Deallocate(ptr4); + GetMemoryManager().Deallocate(ptr); + + // empty pools are freed, so no memory should be left + CHECK_EQUAL(0, testAlloc->GetReservedSizeTotal()); + CHECK_EQUAL(0, testAlloc->GetAllocatedMemorySize()); + + GetMemoryManager().RemoveCustomAllocator(testAlloc); + } + #endif + + /* + TEST_FIXTURE(MemoryManagerFixture, TempAllocatePerformance) + { + int allocSizes[16] = {1234,345,763,34768,343,2345,45643,85335,3453,7843,2346,437,3475,23789,423743,4537}; + GetMemoryManager().FrameMaintenance(); + { + ABSOLUTE_TIME start = START_TIME; + for(int j = 0; j < 100000; j++) + { + void* fillptr1 = UNITY_MALLOC(kMemTempAlloc, 128); + void* fillptr2 = UNITY_MALLOC(kMemTempAlloc, 54); + void* fillptr3 = UNITY_MALLOC(kMemTempAlloc, 158); + for(int i = 0; i < 1000; i++) + { + void* ptr = UNITY_MALLOC_ALIGNED(kMemTempAlloc, allocSizes[i%16], 16); + UNITY_FREE(kMemTempAlloc, ptr); + } + UNITY_FREE(kMemTempAlloc, fillptr1); + UNITY_FREE(kMemTempAlloc, fillptr2); + UNITY_FREE(kMemTempAlloc, fillptr3); + } + printf_console("Elapsed time for 1M Temp allocs %.5fs\n", GetElapsedTimeInSeconds (start)/100.f); + } + { + ABSOLUTE_TIME start = START_TIME; + for(int j = 0; j < 1000; j++) + { + void* fillptr1 = UNITY_MALLOC(kMemDefault, 128); + void* fillptr2 = UNITY_MALLOC(kMemDefault, 54); + void* fillptr3 = UNITY_MALLOC(kMemDefault, 158); + for(int i = 0; i < 1000; i++) + { + void* ptr = UNITY_MALLOC_ALIGNED(kMemDefault, allocSizes[i%16], 16); + UNITY_FREE(kMemDefault, ptr); + } + UNITY_FREE(kMemDefault, fillptr1); + UNITY_FREE(kMemDefault, fillptr2); + UNITY_FREE(kMemDefault, fillptr3); + } + printf_console("Elapsed time for 1M DynHeap allocs %.5f s\n", GetElapsedTimeInSeconds (start)); + } + { + ABSOLUTE_TIME start = START_TIME; + for(int j = 0; j < 10; j++) + { + void* fillptr1 = malloc(128); + void* fillptr2 = malloc(154); + void* fillptr3 = malloc(158); + for(int i = 0; i < 1000; i++) + { + void* ptr = malloc(allocSizes[i%16]); + free(ptr); + } + free(fillptr1); + free(fillptr2); + free(fillptr3); + } + printf_console("Elapsed time for 1M malloc/free %.5f s\n", GetElapsedTimeInSeconds (start)*100.f); + } + GetMemoryManager().FrameMaintenance(); + } + */ + +} +#endif +#endif + + +#else // !ENABLE_MEMORY_MANAGER + +void register_external_gfx_allocation(void* ptr, size_t size, size_t related, const char* file, int line) { } +void register_external_gfx_deallocation(void* ptr, const char* file, int line) { } + + +void* malloc_internal(size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + return UNITY_LL_ALLOC(label, size, align); +} + +void* calloc_internal(size_t count, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + void* ptr = UNITY_LL_ALLOC(label, size * count, align); + memset (ptr, 0, size * count); + return ptr; +} + +void* realloc_internal(void* ptr, size_t size, int align, MemLabelRef label, int allocateOptions, const char* file, int line) +{ + return UNITY_LL_REALLOC(label, ptr, size, align); +} + +void free_alloc_internal(void* ptr, MemLabelRef label) +{ + UNITY_LL_FREE(label, ptr); +} + +#if !((UNITY_OSX || UNITY_LINUX) && UNITY_EDITOR) +void* operator new (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); } +void* operator new [] (size_t size) THROWING_NEW_THROW { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); } +void operator delete (void* p) throw() { UNITY_LL_FREE (kMemNewDelete, p); } // can't make allocator assumption, since ptr can be newed by other operator new +void operator delete [] (void* p) throw() {UNITY_LL_FREE (kMemNewDelete, p); } + +void* operator new (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); } +void* operator new [] (size_t size, const std::nothrow_t&) throw() { return UNITY_LL_ALLOC (kMemNewDelete, size, kDefaultMemoryAlignment); }; +void operator delete (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); } +void operator delete [] (void* p, const std::nothrow_t&) throw() { UNITY_LL_FREE (kMemNewDelete, p); } +#endif + +#endif + |