nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: PublicHeader: OVR.h nuclear@1: Filename : OVR_Allocator.h nuclear@1: Content : Installable memory allocator nuclear@1: Created : September 19, 2012 nuclear@1: Notes : nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: ************************************************************************************/ nuclear@1: nuclear@1: #ifndef OVR_Allocator_h nuclear@1: #define OVR_Allocator_h nuclear@1: nuclear@1: #include "OVR_Types.h" nuclear@1: nuclear@1: //----------------------------------------------------------------------------------- nuclear@1: nuclear@1: // ***** Disable template-unfriendly MS VC++ warnings nuclear@1: #if defined(OVR_CC_MSVC) nuclear@1: // Pragma to prevent long name warnings in in VC++ nuclear@1: #pragma warning(disable : 4503) nuclear@1: #pragma warning(disable : 4786) nuclear@1: // In MSVC 7.1, warning about placement new POD default initializer nuclear@1: #pragma warning(disable : 4345) nuclear@1: #endif nuclear@1: nuclear@1: // Un-define new so that placement constructors work nuclear@1: #undef new nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------------- nuclear@1: // ***** Placement new overrides nuclear@1: nuclear@1: // Calls constructor on own memory created with "new(ptr) type" nuclear@1: #ifndef __PLACEMENT_NEW_INLINE nuclear@1: #define __PLACEMENT_NEW_INLINE nuclear@1: nuclear@1: # if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) nuclear@1: # include nuclear@1: # else nuclear@1: // Useful on MSVC nuclear@1: OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } nuclear@1: OVR_FORCE_INLINE void operator delete (void *, void *) { } nuclear@1: # endif nuclear@1: nuclear@1: #endif // __PLACEMENT_NEW_INLINE nuclear@1: nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------ nuclear@1: // ***** Macros to redefine class new/delete operators nuclear@1: nuclear@1: // Types specifically declared to allow disambiguation of address in nuclear@1: // class member operator new. nuclear@1: nuclear@1: #define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ nuclear@1: void* operator new(UPInt sz) \ nuclear@1: { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ nuclear@1: void* operator new(UPInt sz, const char* file, int line) \ nuclear@1: { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ nuclear@1: void operator delete(void *p) \ nuclear@1: { check_delete(class_name, p); OVR_FREE(p); } \ nuclear@1: void operator delete(void *p, const char*, int) \ nuclear@1: { check_delete(class_name, p); OVR_FREE(p); } nuclear@1: nuclear@1: #define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ nuclear@1: void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ nuclear@1: void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } nuclear@1: nuclear@1: nuclear@1: #define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) nuclear@1: nuclear@1: // Redefined all delete/new operators in a class without custom memory initialization nuclear@1: #define OVR_MEMORY_REDEFINE_NEW(class_name) \ nuclear@1: OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) nuclear@1: nuclear@1: nuclear@1: namespace OVR { nuclear@1: nuclear@1: //----------------------------------------------------------------------------------- nuclear@1: // ***** Construct / Destruct nuclear@1: nuclear@1: // Construct/Destruct functions are useful when new is redefined, as they can nuclear@1: // be called instead of placement new constructors. nuclear@1: nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE T* Construct(void *p) nuclear@1: { nuclear@1: return ::new(p) T; nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE T* Construct(void *p, const T& source) nuclear@1: { nuclear@1: return ::new(p) T(source); nuclear@1: } nuclear@1: nuclear@1: // Same as above, but allows for a different type of constructor. nuclear@1: template nuclear@1: OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) nuclear@1: { nuclear@1: return ::new(p) T(source); nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) nuclear@1: { nuclear@1: return ::new(p) T(src1, src2); nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) nuclear@1: { nuclear@1: UByte *pdata = (UByte*)p; nuclear@1: for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) nuclear@1: { nuclear@1: Construct(pdata); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) nuclear@1: { nuclear@1: UByte *pdata = (UByte*)p; nuclear@1: for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) nuclear@1: { nuclear@1: Construct(pdata, source); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE void Destruct(T *pobj) nuclear@1: { nuclear@1: pobj->~T(); nuclear@1: OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. nuclear@1: } nuclear@1: nuclear@1: template nuclear@1: OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) nuclear@1: { nuclear@1: for (UPInt i=0; i~T(); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------------- nuclear@1: // ***** Allocator nuclear@1: nuclear@1: // Allocator defines a memory allocation interface that developers can override nuclear@1: // to to provide memory for OVR; an instance of this class is typically created on nuclear@1: // application startup and passed into System or OVR::System constructor. nuclear@1: // nuclear@1: // nuclear@1: // Users implementing this interface must provide three functions: Alloc, Free, nuclear@1: // and Realloc. Implementations of these functions must honor the requested alignment. nuclear@1: // Although arbitrary alignment requests are possible, requested alignment will nuclear@1: // typically be small, such as 16 bytes or less. nuclear@1: nuclear@1: class Allocator nuclear@1: { nuclear@1: friend class System; nuclear@1: public: nuclear@1: nuclear@1: // *** Standard Alignment Alloc/Free nuclear@1: nuclear@1: // Allocate memory of specified size with default alignment. nuclear@1: // Alloc of size==0 will allocate a tiny block & return a valid pointer; nuclear@1: // this makes it suitable for new operator. nuclear@1: virtual void* Alloc(UPInt size) = 0; nuclear@1: // Same as Alloc, but provides an option of passing debug data. nuclear@1: virtual void* AllocDebug(UPInt size, const char* file, unsigned line) nuclear@1: { OVR_UNUSED2(file, line); return Alloc(size); } nuclear@1: nuclear@1: // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to nuclear@1: // new memory block, which may be the same as original pointer. Will return 0 if reallocation nuclear@1: // failed, in which case previous memory is still valid. nuclear@1: // Realloc to decrease size will never fail. nuclear@1: // Realloc of pointer == 0 is equivalent to Alloc nuclear@1: // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). nuclear@1: virtual void* Realloc(void* p, UPInt newSize) = 0; nuclear@1: nuclear@1: // Frees memory allocated by Alloc/Realloc. nuclear@1: // Free of null pointer is valid and will do nothing. nuclear@1: virtual void Free(void *p) = 0; nuclear@1: nuclear@1: nuclear@1: // *** Standard Alignment Alloc/Free nuclear@1: nuclear@1: // Allocate memory of specified alignment. nuclear@1: // Memory allocated with AllocAligned MUST be freed with FreeAligned. nuclear@1: // Default implementation will delegate to Alloc/Free after doing rounding. nuclear@1: virtual void* AllocAligned(UPInt size, UPInt align); nuclear@1: // Frees memory allocated with AllocAligned. nuclear@1: virtual void FreeAligned(void* p); nuclear@1: nuclear@1: // Returns the pointer to the current globally installed Allocator instance. nuclear@1: // This pointer is used for most of the memory allocations. nuclear@1: static Allocator* GetInstance() { return pInstance; } nuclear@1: nuclear@1: nuclear@1: protected: nuclear@1: // onSystemShutdown is called on the allocator during System::Shutdown. nuclear@1: // At this point, all allocations should've been freed. nuclear@1: virtual void onSystemShutdown() { } nuclear@1: nuclear@1: public: nuclear@1: static void setInstance(Allocator* palloc) nuclear@1: { nuclear@1: OVR_ASSERT((pInstance == 0) || (palloc == 0)); nuclear@1: pInstance = palloc; nuclear@1: } nuclear@1: nuclear@1: private: nuclear@1: nuclear@1: static Allocator* pInstance; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------ nuclear@1: // ***** Allocator_SingletonSupport nuclear@1: nuclear@1: // Allocator_SingletonSupport is a Allocator wrapper class that implements nuclear@1: // the InitSystemSingleton static function, used to create a global singleton nuclear@1: // used for the OVR::System default argument initialization. nuclear@1: // nuclear@1: // End users implementing custom Allocator interface don't need to make use of this base nuclear@1: // class; they can just create an instance of their own class on stack and pass it to System. nuclear@1: nuclear@1: template nuclear@1: class Allocator_SingletonSupport : public Allocator nuclear@1: { nuclear@1: struct AllocContainer nuclear@1: { nuclear@1: UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; nuclear@1: bool Initialized; nuclear@1: AllocContainer() : Initialized(0) { } nuclear@1: }; nuclear@1: nuclear@1: AllocContainer* pContainer; nuclear@1: nuclear@1: public: nuclear@1: Allocator_SingletonSupport() : pContainer(0) { } nuclear@1: nuclear@1: // Creates a singleton instance of this Allocator class used nuclear@1: // on OVR_DEFAULT_ALLOCATOR during System initialization. nuclear@1: static D* InitSystemSingleton() nuclear@1: { nuclear@1: static AllocContainer Container; nuclear@1: OVR_ASSERT(Container.Initialized == false); nuclear@1: nuclear@1: Allocator_SingletonSupport *presult = Construct((void*)Container.Data); nuclear@1: presult->pContainer = &Container; nuclear@1: Container.Initialized = true; nuclear@1: return (D*)presult; nuclear@1: } nuclear@1: nuclear@1: protected: nuclear@1: virtual void onSystemShutdown() nuclear@1: { nuclear@1: Allocator::onSystemShutdown(); nuclear@1: if (pContainer) nuclear@1: { nuclear@1: pContainer->Initialized = false; nuclear@1: Destruct((D*)this); nuclear@1: pContainer = 0; nuclear@1: } nuclear@1: } nuclear@1: }; nuclear@1: nuclear@1: //------------------------------------------------------------------------ nuclear@1: // ***** Default Allocator nuclear@1: nuclear@1: // This allocator is created and used if no other allocator is installed. nuclear@1: // Default allocator delegates to system malloc. nuclear@1: nuclear@1: class DefaultAllocator : public Allocator_SingletonSupport nuclear@1: { nuclear@1: public: nuclear@1: virtual void* Alloc(UPInt size); nuclear@1: virtual void* AllocDebug(UPInt size, const char* file, unsigned line); nuclear@1: virtual void* Realloc(void* p, UPInt newSize); nuclear@1: virtual void Free(void *p); nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------ nuclear@1: // ***** Memory Allocation Macros nuclear@1: nuclear@1: // These macros should be used for global allocation. In the future, these nuclear@1: // macros will allows allocation to be extended with debug file/line information nuclear@1: // if necessary. nuclear@1: nuclear@1: #define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) nuclear@1: #define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) nuclear@1: #define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) nuclear@1: #define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) nuclear@1: nuclear@1: #ifdef OVR_BUILD_DEBUG nuclear@1: #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) nuclear@1: #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) nuclear@1: #else nuclear@1: #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) nuclear@1: #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) nuclear@1: #endif nuclear@1: nuclear@1: //------------------------------------------------------------------------ nuclear@1: nuclear@1: // Base class that overrides the new and delete operators. nuclear@1: // Deriving from this class, even as a multiple base, incurs no space overhead. nuclear@1: class NewOverrideBase nuclear@1: { nuclear@1: public: nuclear@1: nuclear@1: // Redefine all new & delete operators. nuclear@1: OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: } // OVR nuclear@1: nuclear@1: nuclear@1: // Redefine operator 'new' if necessary. nuclear@1: #if defined(OVR_DEFINE_NEW) nuclear@1: #define new OVR_DEFINE_NEW nuclear@1: #endif nuclear@1: nuclear@1: nuclear@1: #endif // OVR_Memory