243 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| //-----------------------------------------------------------------------------
 | |
| // Torque Game Engine
 | |
| // Copyright (C) GarageGames.com, Inc.
 | |
| //-----------------------------------------------------------------------------
 | |
| 
 | |
| #ifndef _FRAMEALLOCATOR_H_
 | |
| #define _FRAMEALLOCATOR_H_
 | |
| 
 | |
| #ifndef _PLATFORM_H_
 | |
| #include "platform/platform.h"
 | |
| #endif
 | |
| 
 | |
| /// Temporary memory pool for per-frame allocations.
 | |
| ///
 | |
| /// In the course of rendering a frame, it is often necessary to allocate
 | |
| /// many small chunks of memory, then free them all in a batch. For instance,
 | |
| /// say we're allocating storage for some vertex calculations:
 | |
| ///
 | |
| /// @code
 | |
| ///   // Get FrameAllocator memory...
 | |
| ///   U32 waterMark = FrameAllocator::getWaterMark();
 | |
| ///   F32 * ptr = (F32*)FrameAllocator::alloc(sizeof(F32)*2*targetMesh->vertsPerFrame);
 | |
| ///
 | |
| ///   ... calculations ...
 | |
| ///
 | |
| ///   // Free frameAllocator memory
 | |
| ///   FrameAllocator::setWaterMark(waterMark);
 | |
| /// @endcode
 | |
| class FrameAllocator
 | |
| {
 | |
|    static U8*   smBuffer;
 | |
|    static U32   smHighWaterMark;
 | |
|    static U32   smWaterMark;
 | |
| 
 | |
|   public:
 | |
|    inline static void init(const U32 frameSize);
 | |
|    inline static void destroy();
 | |
| 
 | |
|    inline static void* alloc(const U32 allocSize);
 | |
| 
 | |
|    inline static void setWaterMark(const U32);
 | |
|    inline static U32  getWaterMark();
 | |
|    inline static U32  getHighWaterMark();
 | |
| };
 | |
| 
 | |
| #if defined(TORQUE_DEBUG)
 | |
| static S32 sgMaxFrameAllocation = 0;
 | |
| #endif
 | |
| 
 | |
| void FrameAllocator::init(const U32 frameSize)
 | |
| {
 | |
|    AssertFatal(smBuffer == NULL, "Error, already initialized");
 | |
|    smBuffer = new U8[frameSize];
 | |
|    smWaterMark = 0;
 | |
|    smHighWaterMark = frameSize;
 | |
| }
 | |
| 
 | |
| void FrameAllocator::destroy()
 | |
| {
 | |
|    AssertFatal(smBuffer != NULL, "Error, not initialized");
 | |
| 
 | |
|    delete [] smBuffer;
 | |
|    smBuffer = NULL;
 | |
|    smWaterMark = 0;
 | |
|    smHighWaterMark = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void* FrameAllocator::alloc(const U32 allocSize)
 | |
| {
 | |
|    U32 _allocSize = allocSize;
 | |
| #if defined(FRAMEALLOCATOR_DEBUG_GUARD)
 | |
|    _allocSize+=4;
 | |
| #endif
 | |
|    AssertFatal(smBuffer != NULL, "Error, no buffer!");
 | |
|    AssertFatal(smWaterMark + _allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!");
 | |
| 
 | |
|    U8* p = &smBuffer[smWaterMark];
 | |
|    smWaterMark += _allocSize;
 | |
| 
 | |
| #if defined(TORQUE_DEBUG)
 | |
|    if (smWaterMark > sgMaxFrameAllocation)
 | |
|       sgMaxFrameAllocation = smWaterMark;
 | |
| #endif
 | |
| 
 | |
| #if defined(FRAMEALLOCATOR_DEBUG_GUARD)
 | |
|    U32 *flag = (U32*) &smBuffer[smWaterMark-4];
 | |
|    *flag = 0xdeadbeef ^ smWaterMark;
 | |
| #endif
 | |
|    return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| void FrameAllocator::setWaterMark(const U32 waterMark)
 | |
| {
 | |
|    AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark");
 | |
| 
 | |
| #if defined(FRAMEALLOCATOR_DEBUG_GUARD)
 | |
|    if(smWaterMark >= 4 )
 | |
|    {
 | |
|       U32 *flag = (U32*) &smBuffer[smWaterMark-4];
 | |
|       AssertFatal( *flag == 0xdeadbeef ^ smWaterMark, "FrameAllocator guard overwritten!");
 | |
|    }
 | |
| #endif
 | |
|    smWaterMark = waterMark;
 | |
| }
 | |
| 
 | |
| U32 FrameAllocator::getWaterMark()
 | |
| {
 | |
|    return smWaterMark;
 | |
| }
 | |
| 
 | |
| U32 FrameAllocator::getHighWaterMark()
 | |
| {
 | |
|    return smHighWaterMark;
 | |
| }
 | |
| 
 | |
| /// Helper class to deal with FrameAllocator usage.
 | |
| ///
 | |
| /// The purpose of this class is to make it simpler and more reliable to use the
 | |
| /// FrameAllocator. Simply use it like this:
 | |
| ///
 | |
| /// @code
 | |
| /// FrameAllocatorMarker mem;
 | |
| ///
 | |
| /// char *buff = (char*)mem.alloc(100);
 | |
| /// @endcode
 | |
| ///
 | |
| /// When you leave the scope you defined the FrameAllocatorMarker in, it will
 | |
| /// automatically restore the watermark on the FrameAllocator. In situations
 | |
| /// with complex branches, this can be a significant headache remover, as you
 | |
| /// don't have to remember to reset the FrameAllocator on every posssible branch.
 | |
| class FrameAllocatorMarker
 | |
| {
 | |
|    U32 mMarker;
 | |
| 
 | |
| public:
 | |
|    FrameAllocatorMarker()
 | |
|    {
 | |
|       mMarker = FrameAllocator::getWaterMark();
 | |
|    }
 | |
| 
 | |
|    ~FrameAllocatorMarker()
 | |
|    {
 | |
|       FrameAllocator::setWaterMark(mMarker);
 | |
|    }
 | |
| 
 | |
|    void* alloc(const U32 allocSize) const
 | |
|    {
 | |
|       return FrameAllocator::alloc(allocSize);
 | |
|    }
 | |
| };
 | |
| 
 | |
| /// Class for temporary variables that you want to allocate easily using
 | |
| /// the FrameAllocator. For example:
 | |
| /// @code
 | |
| /// FrameTemp<char> tempStr(32); // NOTE! This parameter is NOT THE SIZE IN BYTES. See constructor docs.
 | |
| /// dStrcat( tempStr, SomeOtherString );
 | |
| /// tempStr[2] = 'l';
 | |
| /// Con::printf( tempStr );
 | |
| /// Con::printf( "Foo: %s", ~tempStr );
 | |
| /// @endcode
 | |
| ///
 | |
| /// This will automatically handle getting and restoring the watermark of the
 | |
| /// FrameAllocator when it goes out of scope. You should notice the strange
 | |
| /// operator infront of tempStr on the printf call. This is normally a unary
 | |
| /// operator for ones-complement, but in this class it will simply return the
 | |
| /// memory of the allocation. It's the same as doing (const char *)tempStr
 | |
| /// in the above case. The reason why it is necessary for the second printf
 | |
| /// and not the first is because the second one is taking a variable arg
 | |
| /// list and so it isn't getting the cast so that it's cast operator can
 | |
| /// properly return the memory instead of the FrameTemp object itself.
 | |
| ///
 | |
| /// @note It is important to note that this object is designed to just be a
 | |
| /// temporary array of a dynamic size. Some wierdness may occur if you try
 | |
| /// do perform crazy pointer stuff with it using regular operators on it.
 | |
| /// I implemented what I thought were the most common operators that it
 | |
| /// would be used for. If strange things happen, you will need to debug
 | |
| /// them yourself.
 | |
| template<class T>
 | |
| class FrameTemp
 | |
| {
 | |
| protected:
 | |
|    U32 mWaterMark;
 | |
|    T *mMemory;
 | |
| 
 | |
| public:
 | |
|    /// Constructor will store the FrameAllocator watermark and allocate the memory off
 | |
|    /// of the FrameAllocator.
 | |
|    ///
 | |
|    /// @note It is important to note that, unlike the FrameAllocatorMarker and the
 | |
|    /// FrameAllocator itself, the argument to allocate is NOT the size in bytes,
 | |
|    /// doing:
 | |
|    /// @code
 | |
|    /// FrameTemp<F64> f64s(5);
 | |
|    /// @endcode
 | |
|    /// Is the same as
 | |
|    /// @code
 | |
|    /// F64 *f64s = new F64[5];
 | |
|    /// @endcode
 | |
|    ///
 | |
|    /// @param   count   The number of objects to allocate
 | |
|    FrameTemp( const U32 count = 1 )
 | |
|    {
 | |
|       AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" );
 | |
|       mWaterMark = FrameAllocator::getWaterMark();
 | |
|       mMemory = static_cast<T *>( FrameAllocator::alloc( sizeof( T ) * count ) );
 | |
|    }
 | |
| 
 | |
|    /// Destructor restores the watermark
 | |
|    ~FrameTemp()
 | |
|    {
 | |
|       FrameAllocator::setWaterMark( mWaterMark );
 | |
|    }
 | |
| 
 | |
|    /// NOTE: This will return the memory, NOT perform a ones-complement
 | |
|    T* operator ~() { return mMemory; };
 | |
|    /// NOTE: This will return the memory, NOT perform a ones-complement
 | |
|    const T* operator ~() const { return mMemory; };
 | |
| 
 | |
|    /// NOTE: This will dereference the memory, NOT do standard unary plus behavior
 | |
|    T& operator +() { return *mMemory; };
 | |
|    /// NOTE: This will dereference the memory, NOT do standard unary plus behavior
 | |
|    const T& operator +() const { return *mMemory; };
 | |
| 
 | |
|    T& operator *() { return *mMemory; };
 | |
|    const T& operator *() const { return *mMemory; };
 | |
| 
 | |
|    T** operator &() { return &mMemory; };
 | |
|    const T** operator &() const { return &mMemory; };
 | |
| 
 | |
|    operator T*() { return mMemory; }
 | |
|    operator const T*() const { return mMemory; }
 | |
| 
 | |
|    operator T&() { return *mMemory; }
 | |
|    operator const T&() const { return *mMemory; }
 | |
| 
 | |
|    operator T() { return *mMemory; }
 | |
|    operator const T() const { return *mMemory; }
 | |
| };
 | |
| 
 | |
| #endif  // _H_FRAMEALLOCATOR_
 | 
