Explorar el Código

Basic refactoring WIP

James Urquhart hace 1 año
padre
commit
915fac31b3

+ 200 - 223
Engine/source/core/dataChunker.h

@@ -1,323 +1,300 @@
 //-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
+// Copyright (c) 2023 tgemit contributors.
+// See AUTHORS file and git repository for contributor information.
 //
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
+// SPDX-License-Identifier: MIT
 //-----------------------------------------------------------------------------
 
-#ifndef _DATACHUNKER_H_
+#pragma once
 #define _DATACHUNKER_H_
 
 #ifndef _PLATFORM_H_
 #  include "platform/platform.h"
 #endif
+#ifndef _PLATFORMASSERT_H_
+#  include "platform/platformAssert.h"
+#endif
+
+#include <algorithm>
+#include <stdint.h>
+#include "core/frameAllocator.h"
+//#include "math/mMathFn.h" // tgemit - needed here for the moment
 
-//----------------------------------------------------------------------------
 /// Implements a chunked data allocator.
 ///
-/// Calling new/malloc all the time is a time consuming operation. Therefore,
-/// we provide the DataChunker, which allocates memory in blocks of
-/// chunkSize (by default 16k, see ChunkSize, though it can be set in
-/// the constructor), then doles it out as requested, in chunks of up to
-/// chunkSize in size.
-///
-/// It will assert if you try to get more than ChunkSize bytes at a time,
-/// and it deals with the logic of allocating new blocks and giving out
-/// word-aligned chunks.
+/// This memory allocator allocates data in chunks of bytes, 
+/// the default size being ChunkSize.
+/// Bytes are sourced from the current head chunk until expended, 
+/// in which case a new chunk of bytes will be allocated from 
+/// the system memory allocator.
 ///
-/// Note that new/free/realloc WILL NOT WORK on memory gotten from the
-/// DataChunker. This also only grows (you can call freeBlocks to deallocate
-/// and reset things).
-class DataChunker
+template<class T> class BaseDataChunker
 {
 public:
-   /// Block of allocated memory.
-   ///
-   /// <b>This has nothing to do with datablocks as used in the rest of Torque.</b>
-   struct DataBlock
+   enum
    {
-      DataBlock* next;        ///< linked list pointer to the next DataBlock for this chunker
-      S32 curIndex;           ///< current allocation point within this DataBlock
-      DataBlock();
-      ~DataBlock();
-      inline U8 *getData();
+      ChunkSize = 16384
    };
 
-   enum {
-      PaddDBSize = (sizeof(DataBlock) + 3) & ~3, ///< Padded size of DataBlock
-      ChunkSize = 16384 - PaddDBSize ///< Default size of each DataBlock page in the DataChunker
+   struct alignas(uintptr_t) DataBlock : public AlignedBufferAllocator<T>
+   {
+      DataBlock* mNext;
+
+      inline DataBlock* getEnd()
+      {
+         return this + 1;
+      }
    };
 
-   /// Return a pointer to a chunk of memory from a pre-allocated block.
-   ///
-   /// This memory goes away when you call freeBlocks.
-   ///
-   /// This memory is word-aligned.
-   /// @param   size    Size of chunk to return. This must be less than chunkSize or else
-   ///                  an assertion will occur.
-   void *alloc(S32 size);
-
-   /// Free all allocated memory blocks.
-   ///
-   /// This invalidates all pointers returned from alloc().
-   void freeBlocks(bool keepOne = false);
-
-   /// Initialize using blocks of a given size.
-   ///
-   /// One new block is allocated at constructor-time.
-   ///
-   /// @param   size    Size in bytes of the space to allocate for each block.
-   DataChunker(S32 size=ChunkSize);
-   ~DataChunker();
-
-   /// Swaps the memory allocated in one data chunker for another.  This can be used to implement
-   /// packing of memory stored in a DataChunker.
-   void swap(DataChunker &d)
+protected:
+   dsize_t mChunkSize;
+   DataBlock* mChunkHead;
+
+public:
+
+   BaseDataChunker(U32 chunkSize = BaseDataChunker<T>::ChunkSize) : mChunkSize(chunkSize), mChunkHead(NULL)
    {
-      DataBlock *temp = d.mCurBlock;
-      d.mCurBlock = mCurBlock;
-      mCurBlock = temp;
    }
-   
-public:
+
+   virtual ~BaseDataChunker()
+   {
+      freeBlocks(false);
+   }
+
+   DataBlock* allocChunk(dsize_t chunkSize)
+   {
+      DataBlock* newChunk = (DataBlock*)dMalloc(sizeof(DataBlock) + chunkSize);
+      constructInPlace(newChunk);
+      newChunk->initWithBytes((T*)newChunk->getEnd(), chunkSize);
+      newChunk->mNext = mChunkHead;
+      mChunkHead = newChunk;
+      return newChunk;
+   }
+
+   void* alloc(dsize_t numBytes)
+   {
+      void* theAlloc = mChunkHead ? mChunkHead->allocBytes(numBytes) : NULL;
+      if (theAlloc == NULL)
+      {
+         dsize_t actualSize = std::max<dsize_t>(mChunkSize, numBytes);
+         allocChunk(actualSize);
+         theAlloc = mChunkHead->allocBytes(numBytes);
+         AssertFatal(theAlloc != NULL, "Something really odd going on here");
+      }
+      return theAlloc;
+   }
+
+   void freeBlocks(bool keepOne = false)
+   {
+      DataBlock* itr = mChunkHead;
+      while (itr)
+      {
+         DataBlock* nextItr = itr->mNext;
+         if (nextItr == NULL && keepOne)
+         {
+            itr->setPosition(0);
+            break;
+         }
+         dFree(itr);
+         itr = nextItr;
+      }
+      mChunkHead = itr;
+   }
+
    U32 countUsedBlocks()
    {
       U32 count = 0;
-      if (!mCurBlock)
-         return 0;
-      for (DataBlock *ptr = mCurBlock; ptr != NULL; ptr = ptr->next)
+      for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext)
       {
          count++;
       }
       return count;
    }
-   
-   void setChunkSize(U32 size)
+
+   dsize_t countUsedBytes()
    {
-      AssertFatal(mCurBlock == NULL, "Cant resize now");
-      mChunkSize = size;
+      dsize_t count = 0;
+      for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext)
+      {
+         count += itr->getPositionBytes();
+      }
+      return count;
    }
 
-   
-public:
-
-   DataBlock*  mCurBlock;    ///< current page we're allocating data from.  If the
-                              ///< data size request is greater than the memory space currently
-                              ///< available in the current page, a new page will be allocated.
-   S32         mChunkSize;    ///< The size allocated for each page in the DataChunker
+   void setChunkSize(dsize_t size)
+   {
+      AssertFatal(mChunkHead == NULL, "Tried setting AFTER init");
+      mChunkSize = size;
+   }
 };
 
-
-inline U8 *DataChunker::DataBlock::getData()
-{
-   return (U8*)this + DataChunker::PaddDBSize;
-}
-
-//----------------------------------------------------------------------------
-
-template<class T>
-class Chunker: private DataChunker
+class DataChunker : public BaseDataChunker<uintptr_t>
 {
 public:
-   Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
-   T* alloc()  { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
-   void clear()  { freeBlocks(); }
+   DataChunker() : BaseDataChunker<uintptr_t>(BaseDataChunker<uintptr_t>::ChunkSize) { ; }
+   explicit DataChunker(dsize_t size) : BaseDataChunker<uintptr_t>(size) { ; }
 };
 
-//----------------------------------------------------------------------------
-/// This class is similar to the Chunker<> class above.  But it allows for multiple
-/// types of structs to be stored.  
-/// CodeReview:  This could potentially go into DataChunker directly, but I wasn't sure if 
-/// CodeReview:  That would be polluting it.  BTR
-class MultiTypedChunker : private DataChunker
-{
-public:
-   MultiTypedChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
-
-   /// Use like so:  MyType* t = chunker.alloc<MyType>();
-   template<typename T>
-   T* alloc()  { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
-   void clear()  { freeBlocks(true); }
-};
 
-//----------------------------------------------------------------------------
 
-/// Templatized data chunker class with proper construction and destruction of its elements.
-///
-/// DataChunker just allocates space. This subclass actually constructs/destructs the
-/// elements. This class is appropriate for more complex classes.
-template<class T>
-class ClassChunker: private DataChunker
+/// Implements a derivative of BaseDataChunker designed for 
+/// allocating structs of type T without initialization.
+template<class T> class Chunker : private BaseDataChunker<T>
 {
 public:
-   ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size)
+   Chunker(dsize_t size = BaseDataChunker<T>::ChunkSize) : BaseDataChunker<T>(std::max(sizeof(T), size))
    {
-      mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
-      mFreeListHead = NULL;
    }
 
-   /// Allocates and properly constructs in place a new element.
-   T *alloc()
+   T* alloc()
    {
-      if(mFreeListHead == NULL)
-         return constructInPlace(reinterpret_cast<T*>(DataChunker::alloc(mElementSize)));
-      T* ret = mFreeListHead;
-      mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
-      return constructInPlace(ret);
+      return (T*)BaseDataChunker<T>::alloc(sizeof(T));
    }
 
-   /// Properly destructs and frees an element allocated with the alloc method.
-   void free(T* elem)
+   void clear()
    {
-      destructInPlace(elem);
-      *(reinterpret_cast<T**>(elem)) = mFreeListHead;
-      mFreeListHead = elem;
+      BaseDataChunker<T>::freeBlocks();
    }
-
-   void freeBlocks( bool keepOne = false ) 
-   { 
-      DataChunker::freeBlocks( keepOne ); 
-      mFreeListHead = NULL;
-   }
-
-private:
-   S32   mElementSize;     ///< the size of each element, or the size of a pointer, whichever is greater
-   T     *mFreeListHead;   ///< a pointer to a linked list of freed elements for reuse
 };
 
-//----------------------------------------------------------------------------
-
-template<class T>
-class FreeListChunker
+/// Implements a derivative of BaseDataChunker designed for 
+/// allocating structs of various types Y without initialization.
+/// @note: this is horribly suboptimal for types not multiples of uintptr_t in size.
+class MultiTypedChunker : private BaseDataChunker<uintptr_t>
 {
 public:
-   FreeListChunker(DataChunker *inChunker)
-      :  mChunker( inChunker ),
-         mOwnChunker( false ),
-         mFreeListHead( NULL )
+   MultiTypedChunker(dsize_t size = BaseDataChunker<uintptr_t>::ChunkSize) : BaseDataChunker<uintptr_t>(std::max<uintptr_t>(sizeof(uintptr_t), size))
    {
-      mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
    }
 
-   FreeListChunker(S32 size = DataChunker::ChunkSize)
-      :  mFreeListHead( NULL )
+   template<typename Y> Y* alloc()
    {
-      mChunker = new DataChunker( size );
-      mOwnChunker = true;
+      return (Y*)BaseDataChunker<uintptr_t>::alloc(sizeof(Y));
+   }
 
-      mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
+   void clear()
+   {
+      BaseDataChunker<uintptr_t>::freeBlocks(true);
    }
+};
 
-   ~FreeListChunker()
+/// Implements a simple linked list for ClassChunker and FreeListChunker.
+template<class T> struct ChunkerFreeClassList
+{
+   ChunkerFreeClassList<T>* mNextList;
+
+   ChunkerFreeClassList() : mNextList(NULL)
    {
-      if ( mOwnChunker )
-         delete mChunker;
    }
 
-   T *alloc()
+   void reset()
    {
-      if(mFreeListHead == NULL)
-         return reinterpret_cast<T*>(mChunker->alloc(mElementSize));
-      T* ret = mFreeListHead;
-      mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
-      return ret;
+      mNextList = NULL;
    }
 
-   void free(T* elem)
+   bool isEmpty()
    {
-      *(reinterpret_cast<T**>(elem)) = mFreeListHead;
-      mFreeListHead = elem;
+      return mNextList == NULL;
    }
 
-   /// Allow people to free all their memory if they want.
-   void freeBlocks( bool keepOne = false )
+   T* pop()
    {
-      mChunker->freeBlocks( keepOne );
-      mFreeListHead = NULL;
+      ChunkerFreeClassList<T>* oldNext = mNextList;
+      mNextList = mNextList ? mNextList->mNextList : NULL;
+      return (T*)oldNext;
    }
-   
-private:
-   DataChunker *mChunker;
-   bool        mOwnChunker;
 
-   S32   mElementSize;
-   T     *mFreeListHead;
+   void push(ChunkerFreeClassList<T>* other)
+   {
+      other->mNextList = mNextList;
+      mNextList = other;
+   }
 };
 
-
-class FreeListChunkerUntyped
+/// Implements a derivative of BaseDataChunker designed for 
+/// allocating structs or classes of type T with initialization.
+template<class T> class ClassChunker : private BaseDataChunker<T>
 {
+protected:
+   ChunkerFreeClassList<T> mFreeListHead;
+
 public:
-   FreeListChunkerUntyped(U32 inElementSize, DataChunker *inChunker)
-      :  mChunker( inChunker ),
-         mOwnChunker( false ),
-         mElementSize( inElementSize ),
-         mFreeListHead( NULL )
+   ClassChunker(dsize_t size = BaseDataChunker<T>::ChunkSize)
    {
+
    }
 
-   FreeListChunkerUntyped(U32 inElementSize, S32 size = DataChunker::ChunkSize)
-      :  mElementSize( inElementSize ),
-         mFreeListHead( NULL )
+   T* alloc()
    {
-      mChunker = new DataChunker( size );
-      mOwnChunker = true;
+      if (mFreeListHead.isEmpty())
+      {
+         return constructInPlace((T*)BaseDataChunker<T>::alloc(sizeof(T)));
+      }
+      else
+      {
+         return constructInPlace(mFreeListHead.pop());
+      }
    }
 
-   ~FreeListChunkerUntyped()
+   void free(T* item)
    {
-      if ( mOwnChunker )
-         delete mChunker; 
+      destructInPlace(item);
+      mFreeListHead.push(reinterpret_cast<ChunkerFreeClassList<T>*>(item));
    }
 
-   void *alloc()
+   void freeBlocks(bool keepOne=false)
    {
-      if(mFreeListHead == NULL)
-         return mChunker->alloc(mElementSize);
-
-      void  *ret = mFreeListHead;
-      mFreeListHead = *(reinterpret_cast<void**>(mFreeListHead));
-      return ret;
+      BaseDataChunker<T>::freeBlocks(keepOne);
    }
+};
 
-   void free(void* elem)
+/// Implements a chunker which uses the data of another BaseDataChunker 
+/// as underlying storage.
+template<class T> class FreeListChunker
+{
+protected:
+   BaseDataChunker<T>* mChunker;
+   bool mOwnsChunker;
+   ChunkerFreeClassList<T> mFreeListHead;
+
+public:
+   FreeListChunker(BaseDataChunker<T>* otherChunker) :
+      mChunker(otherChunker),
+      mOwnsChunker(false)
    {
-      *(reinterpret_cast<void**>(elem)) = mFreeListHead;
-      mFreeListHead = elem;
    }
 
-   // Allow people to free all their memory if they want.
-   void freeBlocks()
+   FreeListChunker(dsize_t size = BaseDataChunker<T>::ChunkSize)
    {
-      mChunker->freeBlocks();
+      mChunker = new BaseDataChunker<T>(size);
+      mOwnsChunker = true;
+   }
 
-      // We have to terminate the freelist as well or else we'll run
-      // into crazy unused memory.
-      mFreeListHead = NULL;
+   BaseDataChunker<T>* getChunker()
+   {
+      return mChunker;
    }
 
-   U32   getElementSize() const { return mElementSize; }
+   T* alloc()
+   {
+      if (mFreeListHead.isEmpty())
+      {
+         return constructInPlace((T*)mChunker->alloc(sizeof(T)));
+      }
+      else
+      {
+         return constructInPlace(mFreeListHead.pop());
+      }
+   }
 
-private:
-   DataChunker *mChunker;
-   bool        mOwnChunker;
+   void free(T* item)
+   {
+      destructInPlace(item);
+      mFreeListHead.push(reinterpret_cast<ChunkerFreeClassList<T>*>(item));
+   }
 
-   const U32   mElementSize;
-   void        *mFreeListHead;
+   void freeBlocks(bool keepOne)
+   {
+      BaseDataChunker<T>::freeBlocks(keepOne);
+   }
 };
-#endif

+ 13 - 7
Engine/source/core/frameAllocator.cpp

@@ -23,15 +23,21 @@
 #include "core/frameAllocator.h"
 #include "console/engineAPI.h"
 
-U8*   FrameAllocator::smBuffer = NULL;
-U32   FrameAllocator::smWaterMark = 0;
-U32   FrameAllocator::smHighWaterMark = 0;
+thread_local FrameAllocator::FrameAllocatorType   FrameAllocator::smMainInstance;
 
-#ifdef TORQUE_DEBUG
-U32   FrameAllocator::smMaxFrameAllocation = 0;
+#ifdef TORQUE_MEM_DEBUG
+thread_local dsize_t   FrameAllocator::smAllocatedBytes;
+#endif
+
+#if defined(TORQUE_DEBUG)
+
+dsize_t FrameAllocator::smMaxFrameAllocation;
 
-DefineEngineFunction(getMaxFrameAllocation, S32, (),,"")
+
+DefineEngineFunction(getMaxFrameAllocation, S32, (), , "")
 {
-   return FrameAllocator::getMaxFrameAllocation();
+   return (S32)FrameAllocator::smMaxFrameAllocation;
 }
+
+
 #endif

+ 206 - 127
Engine/source/core/frameAllocator.h

@@ -1,5 +1,5 @@
 //-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
+// Copyright (c) 2013 GarageGames, LLC
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to
@@ -27,14 +27,112 @@
 #include "platform/platform.h"
 #endif
 
-/// This #define is used by the FrameAllocator to align starting addresses to
-/// be byte aligned to this value. This is important on the 360 and possibly
-/// on other platforms as well. Use this #define anywhere alignment is needed.
-///
-/// NOTE: Do not change this value per-platform unless you have a very good
-/// reason for doing so. It has the potential to cause inconsistencies in 
-/// memory which is allocated and expected to be contiguous.
-#define FRAMEALLOCATOR_BYTE_ALIGNMENT 4
+template<typename T> class AlignedBufferAllocator
+{
+protected:
+   T* mBuffer;
+   U32 mHighWaterMark;
+   U32 mWaterMark;
+
+public:
+
+   typedef T ValueType;
+
+   AlignedBufferAllocator() : mBuffer(NULL), mHighWaterMark(0), mWaterMark(0)
+   {
+   }
+
+   inline void initWithElements(T* ptr, U32 numElements)
+   {
+      mBuffer = ptr;
+      mHighWaterMark = numElements;
+      mWaterMark = 0;
+   }
+
+   inline void initWithBytes(T* ptr, dsize_t bytes)
+   {
+      mBuffer = ptr;
+      mHighWaterMark = (U32)(calcMaxElementSize(bytes));
+      mWaterMark = 0;
+   }
+
+   inline T* allocBytes(const size_t numBytes)
+   {
+      T* ptr = &mBuffer[mWaterMark];
+      size_t numElements = calcRequiredElementSize(numBytes);
+      if (((size_t)mWaterMark + (size_t)numElements) > (size_t)mHighWaterMark) // safety check
+      {
+#ifdef TORQUE_MEM_DEBUG
+         AssertFatal(false, "Overflow");
+#endif
+         return NULL;
+      }
+      mWaterMark += (U32)numElements;
+      return ptr;
+   }
+
+   inline T* allocElements(const U32 numElements)
+   {
+      T* ptr = &mBuffer[mWaterMark];
+      if (((size_t)mWaterMark + (size_t)numElements) > (size_t)mHighWaterMark) // safety check
+      {
+#ifdef TORQUE_MEM_DEBUG
+         AssertFatal(false, "Overflow");
+#endif
+         return NULL;
+      }
+      mWaterMark += numElements;
+      return ptr;
+   }
+
+   inline void setPosition(const U32 waterMark)
+   {
+      AssertFatal(waterMark <= mHighWaterMark, "Error, invalid waterMark");
+      mWaterMark = waterMark;
+   }
+
+   /// Calculates maximum elements required to store numBytes bytes (may overshoot)
+   static inline U32 calcRequiredElementSize(const dsize_t numBytes)
+   {
+      return (U32)((numBytes + (sizeof(T) - 1)) / sizeof(T));
+   }
+
+   /// Calculates maximum elements required to store numBytes bytes
+   static inline U32 calcMaxElementSize(const dsize_t numBytes)
+   {
+      return (U32)(numBytes / sizeof(T));
+   }
+
+   inline T* getAlignedBuffer() const
+   {
+      return mBuffer;
+   }
+
+   inline U32 getPosition() const
+   {
+      return mWaterMark;
+   }
+
+   inline U32 getSize() const
+   {
+      return mHighWaterMark;
+   }
+
+   inline U32 getElementsLeft() const
+   {
+      return mHighWaterMark - mWaterMark;
+   }
+
+   inline dsize_t getPositionBytes() const
+   {
+      return mWaterMark * sizeof(T);
+   }
+
+   inline dsize_t getSizeBytes() const
+   {
+      return mHighWaterMark * sizeof(T);
+   }
+};
 
 /// Temporary memory pool for per-frame allocations.
 ///
@@ -54,90 +152,76 @@
 /// @endcode
 class FrameAllocator
 {
-   static U8*   smBuffer;
-   static U32   smHighWaterMark;
-   static U32   smWaterMark;
-
-#ifdef TORQUE_DEBUG
-   static U32 smMaxFrameAllocation;
+public:
+   static dsize_t   smMaxFrameAllocation;
+#ifdef TORQUE_MEM_DEBUG
+   static thread_local dsize_t   smAllocatedBytes;
 #endif
+   typedef AlignedBufferAllocator<U32> FrameAllocatorType;
 
-  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();
-
-#ifdef TORQUE_DEBUG
-   static U32 getMaxFrameAllocation() { return smMaxFrameAllocation; }
-#endif
-};
+   inline static void init(const U32 frameSize)
+   {
+      FrameAllocatorType::ValueType* curPtr = smMainInstance.getAlignedBuffer();
+      AssertFatal(curPtr == NULL, "Error, already initialized");
+      if (curPtr)
+         return;
 
-void FrameAllocator::init(const U32 frameSize)
-{
-#ifdef FRAMEALLOCATOR_DEBUG_GUARD
-   AssertISV( false, "FRAMEALLOCATOR_DEBUG_GUARD has been removed because it allows non-contiguous memory allocation by the FrameAllocator, and this is *not* ok." );
+#ifdef TORQUE_MEM_DEBUG
+      smAllocatedBytes = 0;
 #endif
 
-   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;
-
-   AssertFatal(smBuffer != NULL, "Error, no buffer!");
-   AssertFatal(smWaterMark + _allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!");
-   smWaterMark = ( smWaterMark + ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ) & (~( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ));
+      U32 elementSize = FrameAllocatorType::calcRequiredElementSize(frameSize);
+      FrameAllocatorType::ValueType* newAlignedBuffer = new FrameAllocatorType::ValueType[elementSize];
+      smMainInstance.initWithElements(newAlignedBuffer, elementSize);
+   }
 
-   // Sanity check.
-   AssertFatal( !( smWaterMark & ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ), "Frame allocation is not on a specified byte boundry." );
+   inline static void destroy()
+   {
+      FrameAllocatorType::ValueType* curPtr = smMainInstance.getAlignedBuffer();
+      AssertFatal(smMainInstance.getAlignedBuffer() != NULL, "Error, not initialized");
+      if (curPtr == NULL)
+         return;
 
-   U8* p = &smBuffer[smWaterMark];
-   smWaterMark += _allocSize;
+      delete[] curPtr;
+      smMainInstance.initWithElements(NULL, 0);
+   }
 
-#ifdef TORQUE_DEBUG
-   if (smWaterMark > smMaxFrameAllocation)
-      smMaxFrameAllocation = smWaterMark;
+   inline static void* alloc(const U32 allocSize)
+   {
+      void* outPtr = smMainInstance.allocBytes(allocSize);
+
+#ifdef TORQUE_MEM_DEBUG
+      smAllocatedBytes += allocSize;
+      if (smAllocatedBytes > smMaxFrameAllocation)
+      {
+         smMaxFrameAllocation = smAllocatedBytes;
+      }
 #endif
 
-   return p;
-}
+      return outPtr;
+   }
 
+   inline static void setWaterMark(const U32 waterMark)
+   {
+#ifdef TORQUE_MEM_DEBUG
+      AssertFatal(waterMark % sizeof(FrameAllocatorType::ValueType) == 0, "Misaligned watermark");
+      smAllocatedBytes = waterMark;
+#endif
+      smMainInstance.setPosition(waterMark / sizeof(FrameAllocatorType::ValueType));
+   }
 
-void FrameAllocator::setWaterMark(const U32 waterMark)
-{
-   AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark");
-   smWaterMark = waterMark;
-}
+   inline static U32  getWaterMark()
+   {
+      return smMainInstance.getPositionBytes();
+   }
 
-U32 FrameAllocator::getWaterMark()
-{
-   return smWaterMark;
-}
+   inline static U32  getHighWaterMark()
+   {
+      return smMainInstance.getSizeBytes();
+   }
 
-U32 FrameAllocator::getHighWaterMark()
-{
-   return smHighWaterMark;
-}
+   static thread_local FrameAllocatorType smMainInstance;
+};
 
 /// Helper class to deal with FrameAllocator usage.
 ///
@@ -173,19 +257,13 @@ public:
    {
       return FrameAllocator::alloc(allocSize);
    }
-
-   template<typename T>
-   T* alloc(const U32 numElements) const
-   {
-      return reinterpret_cast<T *>(FrameAllocator::alloc(numElements * sizeof(T)));
-   }
 };
 
 /// 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, 32 * sizeof(char) );
+/// dStrcat( tempStr, SomeOtherString );
 /// tempStr[2] = 'l';
 /// Con::printf( tempStr );
 /// Con::printf( "Foo: %s", ~tempStr );
@@ -193,7 +271,7 @@ public:
 ///
 /// This will automatically handle getting and restoring the watermark of the
 /// FrameAllocator when it goes out of scope. You should notice the strange
-/// operator in front of tempStr on the printf call. This is normally a unary
+/// 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
@@ -203,13 +281,16 @@ public:
 ///
 /// @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
-/// to perform crazy pointer stuff with it using regular operators on it.
+/// 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;
+   T* mMemory;
    U32 mNumObjectsInMemory;
 
 public:
@@ -228,26 +309,28 @@ public:
    /// @endcode
    ///
    /// @param   count   The number of objects to allocate
-   FrameTemp( const U32 count = 1 ) : mNumObjectsInMemory( count )
+   FrameTemp(const U32 count = 1) : mNumObjectsInMemory(count)
    {
-      AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" );
+      AssertFatal(count > 0, "Allocating a FrameTemp with less than one instance");
       mWaterMark = FrameAllocator::getWaterMark();
-      mMemory = reinterpret_cast<T *>( FrameAllocator::alloc( sizeof( T ) * count ) );
+      mMemory = reinterpret_cast<T*>(FrameAllocator::alloc(sizeof(T) * count));
 
-      for( S32 i = 0; i < mNumObjectsInMemory; i++ )
-         constructInPlace<T>( &mMemory[i] );
+      for (U32 i = 0; i < mNumObjectsInMemory; i++)
+         constructInPlace<T>(&mMemory[i]);
    }
 
    /// Destructor restores the watermark
    ~FrameTemp()
    {
       // Call destructor
-      for( S32 i = 0; i < mNumObjectsInMemory; i++ )
-         destructInPlace<T>( &mMemory[i] );
+      for (U32 i = 0; i < mNumObjectsInMemory; i++)
+         destructInPlace<T>(&mMemory[i]);
 
-      FrameAllocator::setWaterMark( mWaterMark );
+      FrameAllocator::setWaterMark(mWaterMark);
    }
 
+   U32 getObjectCount(void) const { return mNumObjectsInMemory; }
+
    /// 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
@@ -264,44 +347,40 @@ public:
    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; }
+   operator T& () { return *mMemory; }
+   operator const T& () const { return *mMemory; }
 
    operator T() { return *mMemory; }
-   operator const T() const { return *mMemory; }
-   
-   T& operator []( U32 i ) { return mMemory[ i ]; }
-   const T& operator []( U32 i ) const { return mMemory[ i ]; }
-
-   T& operator []( S32 i ) { return mMemory[ i ]; }
-   const T& operator []( S32 i ) const { return mMemory[ i ]; }
-
-   /// @name Vector-like Interface
-   /// @{
-   T *address() const { return mMemory; }
-   dsize_t size() const { return mNumObjectsInMemory; }
-   /// @}
+   operator const T() const { return *mMemory;
+
+   inline T* address() const { return mMemory; }
+
+   // This ifdef is to satisfy the ever so pedantic GCC compiler
+   //  Which seems to upset visual studio.
+   T& operator[](const U32 idx) { return mMemory[idx]; }
+   const T& operator[](const U32 idx) const { return mMemory[idx]; }
+   T& operator[](const S32 idx) { return mMemory[idx]; }
+   const T& operator[](const S32 idx) const { return mMemory[idx]; }
 };
 
 //-----------------------------------------------------------------------------
 // FrameTemp specializations for types with no constructor/destructor
 #define FRAME_TEMP_NC_SPEC(type) \
-   template<> \
-   inline FrameTemp<type>::FrameTemp( const U32 count ) \
-   { \
-      AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \
-      mWaterMark = FrameAllocator::getWaterMark(); \
-      mMemory = reinterpret_cast<type *>( FrameAllocator::alloc( sizeof( type ) * count ) ); \
-      mNumObjectsInMemory = 0; \
-   } \
-   template<>\
-   inline FrameTemp<type>::~FrameTemp() \
-   { \
-      FrameAllocator::setWaterMark( mWaterMark ); \
-   } \
+template<> \
+inline FrameTemp<type>::FrameTemp( const U32 count ) \
+{ \
+AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \
+mWaterMark = FrameAllocator::getWaterMark(); \
+mMemory = reinterpret_cast<type *>( FrameAllocator::alloc( sizeof( type ) * count ) ); \
+} \
+template<>\
+inline FrameTemp<type>::~FrameTemp() \
+{ \
+FrameAllocator::setWaterMark( mWaterMark ); \
+} \
 
 FRAME_TEMP_NC_SPEC(char);
 FRAME_TEMP_NC_SPEC(float);

+ 1 - 0
Engine/source/core/resource.h

@@ -62,6 +62,7 @@ class ResourceHolderBase
 public:
    static FreeListChunker<ResourceHolderBase> smHolderFactory;
 
+   ResourceHolderBase() : mRes(NULL) { ; } // @note this is needed for the chunked allocator
    virtual ~ResourceHolderBase() {}
    
    // Return void pointer to resource data.