From 915fac31b372fad597510bd2768c1b6e70e42f84 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 29 Oct 2023 00:38:37 +0100 Subject: [PATCH] Basic refactoring WIP --- Engine/source/core/dataChunker.h | 429 ++++++++++++-------------- Engine/source/core/frameAllocator.cpp | 26 +- Engine/source/core/frameAllocator.h | 341 ++++++++++++-------- Engine/source/core/resource.h | 1 + 4 files changed, 430 insertions(+), 367 deletions(-) diff --git a/Engine/source/core/dataChunker.h b/Engine/source/core/dataChunker.h index 3394dd27a..247793650 100644 --- a/Engine/source/core/dataChunker.h +++ b/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 +#include +#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. +/// 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. /// -/// 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. -/// -/// 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 BaseDataChunker { public: - /// Block of allocated memory. - /// - /// This has nothing to do with datablocks as used in the rest of Torque. - 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 + { + 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); +protected: + dsize_t mChunkSize; + DataBlock* mChunkHead; - /// 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) - { - DataBlock *temp = d.mCurBlock; - d.mCurBlock = mCurBlock; - mCurBlock = temp; - } - public: + + BaseDataChunker(U32 chunkSize = BaseDataChunker::ChunkSize) : mChunkSize(chunkSize), mChunkHead(NULL) + { + } + + 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(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"); + dsize_t count = 0; + for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext) + { + count += itr->getPositionBytes(); + } + return count; + } + + void setChunkSize(dsize_t size) + { + AssertFatal(mChunkHead == NULL, "Tried setting AFTER init"); mChunkSize = size; } +}; - +class DataChunker : public BaseDataChunker +{ 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 + DataChunker() : BaseDataChunker(BaseDataChunker::ChunkSize) { ; } + explicit DataChunker(dsize_t size) : BaseDataChunker(size) { ; } }; -inline U8 *DataChunker::DataBlock::getData() -{ - return (U8*)this + DataChunker::PaddDBSize; -} -//---------------------------------------------------------------------------- - -template -class Chunker: private DataChunker +/// Implements a derivative of BaseDataChunker designed for +/// allocating structs of type T without initialization. +template class Chunker : private BaseDataChunker { public: - Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {}; - T* alloc() { return reinterpret_cast(DataChunker::alloc(S32(sizeof(T)))); } - void clear() { freeBlocks(); } + Chunker(dsize_t size = BaseDataChunker::ChunkSize) : BaseDataChunker(std::max(sizeof(T), size)) + { + } + + T* alloc() + { + return (T*)BaseDataChunker::alloc(sizeof(T)); + } + + void clear() + { + BaseDataChunker::freeBlocks(); + } }; -//---------------------------------------------------------------------------- -/// 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 +/// 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 { public: - MultiTypedChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {}; + MultiTypedChunker(dsize_t size = BaseDataChunker::ChunkSize) : BaseDataChunker(std::max(sizeof(uintptr_t), size)) + { + } - /// Use like so: MyType* t = chunker.alloc(); - template - T* alloc() { return reinterpret_cast(DataChunker::alloc(S32(sizeof(T)))); } - void clear() { freeBlocks(true); } + template Y* alloc() + { + return (Y*)BaseDataChunker::alloc(sizeof(Y)); + } + + void clear() + { + BaseDataChunker::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 ClassChunker: private DataChunker +/// Implements a simple linked list for ClassChunker and FreeListChunker. +template struct ChunkerFreeClassList { -public: - ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) + ChunkerFreeClassList* mNextList; + + ChunkerFreeClassList() : mNextList(NULL) { - mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); - mFreeListHead = NULL; } - /// Allocates and properly constructs in place a new element. - T *alloc() + void reset() { - if(mFreeListHead == NULL) - return constructInPlace(reinterpret_cast(DataChunker::alloc(mElementSize))); - T* ret = mFreeListHead; - mFreeListHead = *(reinterpret_cast(mFreeListHead)); - return constructInPlace(ret); + mNextList = NULL; } - /// Properly destructs and frees an element allocated with the alloc method. - void free(T* elem) + bool isEmpty() { - destructInPlace(elem); - *(reinterpret_cast(elem)) = mFreeListHead; - mFreeListHead = elem; + return mNextList == NULL; } - void freeBlocks( bool keepOne = false ) - { - DataChunker::freeBlocks( keepOne ); - mFreeListHead = NULL; + T* pop() + { + ChunkerFreeClassList* oldNext = mNextList; + mNextList = mNextList ? mNextList->mNextList : NULL; + return (T*)oldNext; } -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 + void push(ChunkerFreeClassList* other) + { + other->mNextList = mNextList; + mNextList = other; + } }; -//---------------------------------------------------------------------------- - -template -class FreeListChunker +/// Implements a derivative of BaseDataChunker designed for +/// allocating structs or classes of type T with initialization. +template class ClassChunker : private BaseDataChunker { +protected: + ChunkerFreeClassList mFreeListHead; + public: - FreeListChunker(DataChunker *inChunker) - : mChunker( inChunker ), - mOwnChunker( false ), - mFreeListHead( NULL ) + ClassChunker(dsize_t size = BaseDataChunker::ChunkSize) { - mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); + } - FreeListChunker(S32 size = DataChunker::ChunkSize) - : mFreeListHead( NULL ) + T* alloc() { - mChunker = new DataChunker( size ); - mOwnChunker = true; - - mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); + if (mFreeListHead.isEmpty()) + { + return constructInPlace((T*)BaseDataChunker::alloc(sizeof(T))); + } + else + { + return constructInPlace(mFreeListHead.pop()); + } } - ~FreeListChunker() + void free(T* item) { - if ( mOwnChunker ) - delete mChunker; + destructInPlace(item); + mFreeListHead.push(reinterpret_cast*>(item)); } - T *alloc() + void freeBlocks(bool keepOne=false) { - if(mFreeListHead == NULL) - return reinterpret_cast(mChunker->alloc(mElementSize)); - T* ret = mFreeListHead; - mFreeListHead = *(reinterpret_cast(mFreeListHead)); - return ret; + BaseDataChunker::freeBlocks(keepOne); } - - void free(T* elem) - { - *(reinterpret_cast(elem)) = mFreeListHead; - mFreeListHead = elem; - } - - /// Allow people to free all their memory if they want. - void freeBlocks( bool keepOne = false ) - { - mChunker->freeBlocks( keepOne ); - mFreeListHead = NULL; - } - -private: - DataChunker *mChunker; - bool mOwnChunker; - - S32 mElementSize; - T *mFreeListHead; }; - -class FreeListChunkerUntyped +/// Implements a chunker which uses the data of another BaseDataChunker +/// as underlying storage. +template class FreeListChunker { +protected: + BaseDataChunker* mChunker; + bool mOwnsChunker; + ChunkerFreeClassList mFreeListHead; + public: - FreeListChunkerUntyped(U32 inElementSize, DataChunker *inChunker) - : mChunker( inChunker ), - mOwnChunker( false ), - mElementSize( inElementSize ), - mFreeListHead( NULL ) + FreeListChunker(BaseDataChunker* otherChunker) : + mChunker(otherChunker), + mOwnsChunker(false) { } - FreeListChunkerUntyped(U32 inElementSize, S32 size = DataChunker::ChunkSize) - : mElementSize( inElementSize ), - mFreeListHead( NULL ) + FreeListChunker(dsize_t size = BaseDataChunker::ChunkSize) { - mChunker = new DataChunker( size ); - mOwnChunker = true; + mChunker = new BaseDataChunker(size); + mOwnsChunker = true; } - ~FreeListChunkerUntyped() + BaseDataChunker* getChunker() { - if ( mOwnChunker ) - delete mChunker; + return mChunker; } - void *alloc() + T* alloc() { - if(mFreeListHead == NULL) - return mChunker->alloc(mElementSize); - - void *ret = mFreeListHead; - mFreeListHead = *(reinterpret_cast(mFreeListHead)); - return ret; + if (mFreeListHead.isEmpty()) + { + return constructInPlace((T*)mChunker->alloc(sizeof(T))); + } + else + { + return constructInPlace(mFreeListHead.pop()); + } } - void free(void* elem) + void free(T* item) { - *(reinterpret_cast(elem)) = mFreeListHead; - mFreeListHead = elem; + destructInPlace(item); + mFreeListHead.push(reinterpret_cast*>(item)); } - // Allow people to free all their memory if they want. - void freeBlocks() + void freeBlocks(bool keepOne) { - mChunker->freeBlocks(); - - // We have to terminate the freelist as well or else we'll run - // into crazy unused memory. - mFreeListHead = NULL; + BaseDataChunker::freeBlocks(keepOne); } - - U32 getElementSize() const { return mElementSize; } - -private: - DataChunker *mChunker; - bool mOwnChunker; - - const U32 mElementSize; - void *mFreeListHead; }; -#endif diff --git a/Engine/source/core/frameAllocator.cpp b/Engine/source/core/frameAllocator.cpp index 5c9530271..7f0435194 100644 --- a/Engine/source/core/frameAllocator.cpp +++ b/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_MEM_DEBUG +thread_local dsize_t FrameAllocator::smAllocatedBytes; +#endif + +#if defined(TORQUE_DEBUG) + +dsize_t FrameAllocator::smMaxFrameAllocation; + + +DefineEngineFunction(getMaxFrameAllocation, S32, (), , "") +{ + return (S32)FrameAllocator::smMaxFrameAllocation; +} + -#ifdef TORQUE_DEBUG -U32 FrameAllocator::smMaxFrameAllocation = 0; - -DefineEngineFunction(getMaxFrameAllocation, S32, (),,"") -{ - return FrameAllocator::getMaxFrameAllocation(); -} #endif diff --git a/Engine/source/core/frameAllocator.h b/Engine/source/core/frameAllocator.h index 98bf8b110..807359946 100644 --- a/Engine/source/core/frameAllocator.h +++ b/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 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,91 +152,77 @@ /// @endcode class FrameAllocator { - static U8* smBuffer; - static U32 smHighWaterMark; - static U32 smWaterMark; +public: + static dsize_t smMaxFrameAllocation; +#ifdef TORQUE_MEM_DEBUG + static thread_local dsize_t smAllocatedBytes; +#endif + typedef AlignedBufferAllocator FrameAllocatorType; -#ifdef TORQUE_DEBUG - static U32 smMaxFrameAllocation; + inline static void init(const U32 frameSize) + { + FrameAllocatorType::ValueType* curPtr = smMainInstance.getAlignedBuffer(); + AssertFatal(curPtr == NULL, "Error, already initialized"); + if (curPtr) + return; + +#ifdef TORQUE_MEM_DEBUG + smAllocatedBytes = 0; #endif - public: - inline static void init(const U32 frameSize); - inline static void destroy(); + U32 elementSize = FrameAllocatorType::calcRequiredElementSize(frameSize); + FrameAllocatorType::ValueType* newAlignedBuffer = new FrameAllocatorType::ValueType[elementSize]; + smMainInstance.initWithElements(newAlignedBuffer, elementSize); + } - inline static void* alloc(const U32 allocSize); + inline static void destroy() + { + FrameAllocatorType::ValueType* curPtr = smMainInstance.getAlignedBuffer(); + AssertFatal(smMainInstance.getAlignedBuffer() != NULL, "Error, not initialized"); + if (curPtr == NULL) + return; - inline static void setWaterMark(const U32); - inline static U32 getWaterMark(); - inline static U32 getHighWaterMark(); + delete[] curPtr; + smMainInstance.initWithElements(NULL, 0); + } -#ifdef TORQUE_DEBUG - static U32 getMaxFrameAllocation() { return smMaxFrameAllocation; } + 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 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)); + } + + inline static U32 getWaterMark() + { + return smMainInstance.getPositionBytes(); + } + + inline static U32 getHighWaterMark() + { + return smMainInstance.getSizeBytes(); + } + + static thread_local FrameAllocatorType smMainInstance; }; -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." ); -#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 )); - - // Sanity check. - AssertFatal( !( smWaterMark & ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ), "Frame allocation is not on a specified byte boundry." ); - - U8* p = &smBuffer[smWaterMark]; - smWaterMark += _allocSize; - -#ifdef TORQUE_DEBUG - if (smWaterMark > smMaxFrameAllocation) - smMaxFrameAllocation = smWaterMark; -#endif - - return p; -} - - -void FrameAllocator::setWaterMark(const U32 waterMark) -{ - AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark"); - 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 @@ -173,19 +257,13 @@ public: { return FrameAllocator::alloc(allocSize); } - - template - T* alloc(const U32 numElements) const - { - return reinterpret_cast(FrameAllocator::alloc(numElements * sizeof(T))); - } }; /// Class for temporary variables that you want to allocate easily using /// the FrameAllocator. For example: /// @code /// FrameTemp 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 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( FrameAllocator::alloc( sizeof( T ) * count ) ); + mMemory = reinterpret_cast(FrameAllocator::alloc(sizeof(T) * count)); - for( S32 i = 0; i < mNumObjectsInMemory; i++ ) - constructInPlace( &mMemory[i] ); + for (U32 i = 0; i < mNumObjectsInMemory; i++) + constructInPlace(&mMemory[i]); } /// Destructor restores the watermark ~FrameTemp() { // Call destructor - for( S32 i = 0; i < mNumObjectsInMemory; i++ ) - destructInPlace( &mMemory[i] ); + for (U32 i = 0; i < mNumObjectsInMemory; i++) + destructInPlace(&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 ]; } + operator const T() const { return *mMemory; - T& operator []( S32 i ) { return mMemory[ i ]; } - const T& operator []( S32 i ) const { return mMemory[ i ]; } + inline T* address() const { return mMemory; } - /// @name Vector-like Interface - /// @{ - T *address() const { return mMemory; } - dsize_t size() const { return mNumObjectsInMemory; } - /// @} + // 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::FrameTemp( const U32 count ) \ - { \ - AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \ - mWaterMark = FrameAllocator::getWaterMark(); \ - mMemory = reinterpret_cast( FrameAllocator::alloc( sizeof( type ) * count ) ); \ - mNumObjectsInMemory = 0; \ - } \ - template<>\ - inline FrameTemp::~FrameTemp() \ - { \ - FrameAllocator::setWaterMark( mWaterMark ); \ - } \ +template<> \ +inline FrameTemp::FrameTemp( const U32 count ) \ +{ \ +AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \ +mWaterMark = FrameAllocator::getWaterMark(); \ +mMemory = reinterpret_cast( FrameAllocator::alloc( sizeof( type ) * count ) ); \ +} \ +template<>\ +inline FrameTemp::~FrameTemp() \ +{ \ +FrameAllocator::setWaterMark( mWaterMark ); \ +} \ FRAME_TEMP_NC_SPEC(char); FRAME_TEMP_NC_SPEC(float); diff --git a/Engine/source/core/resource.h b/Engine/source/core/resource.h index 393e3f372..ad5c81e59 100644 --- a/Engine/source/core/resource.h +++ b/Engine/source/core/resource.h @@ -62,6 +62,7 @@ class ResourceHolderBase public: static FreeListChunker smHolderFactory; + ResourceHolderBase() : mRes(NULL) { ; } // @note this is needed for the chunked allocator virtual ~ResourceHolderBase() {} // Return void pointer to resource data.