frameAllocator.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _FRAMEALLOCATOR_H_
  23. #define _FRAMEALLOCATOR_H_
  24. #ifndef _PLATFORM_H_
  25. #include "platform/platform.h"
  26. #endif
  27. /// Temporary memory pool for per-frame allocations.
  28. ///
  29. /// In the course of rendering a frame, it is often necessary to allocate
  30. /// many small chunks of memory, then free them all in a batch. For instance,
  31. /// say we're allocating storage for some vertex calculations:
  32. ///
  33. /// @code
  34. /// // Get FrameAllocator memory...
  35. /// U32 waterMark = FrameAllocator::getWaterMark();
  36. /// F32 * ptr = (F32*)FrameAllocator::alloc(sizeof(F32)*2*targetMesh->vertsPerFrame);
  37. ///
  38. /// ... calculations ...
  39. ///
  40. /// // Free frameAllocator memory
  41. /// FrameAllocator::setWaterMark(waterMark);
  42. /// @endcode
  43. class FrameAllocator
  44. {
  45. static U8* smBuffer;
  46. static U32 smHighWaterMark;
  47. static U32 smWaterMark;
  48. public:
  49. inline static void init(const U32 frameSize);
  50. inline static void destroy();
  51. inline static void* alloc(const U32 allocSize);
  52. inline static void setWaterMark(const U32);
  53. inline static U32 getWaterMark();
  54. inline static U32 getHighWaterMark();
  55. };
  56. #if defined(TORQUE_DEBUG)
  57. static U32 sgMaxFrameAllocation = 0;
  58. #endif
  59. void FrameAllocator::init(const U32 frameSize)
  60. {
  61. AssertFatal(smBuffer == NULL, "Error, already initialized");
  62. smBuffer = new U8[frameSize];
  63. smWaterMark = 0;
  64. smHighWaterMark = frameSize;
  65. }
  66. void FrameAllocator::destroy()
  67. {
  68. AssertFatal(smBuffer != NULL, "Error, not initialized");
  69. delete [] smBuffer;
  70. smBuffer = NULL;
  71. smWaterMark = 0;
  72. smHighWaterMark = 0;
  73. }
  74. /// This #define is used by the FrameAllocator to align starting addresses to
  75. /// be byte aligned to this value. This is important on the 360 and possibly
  76. /// on other platforms as well. Use this #define anywhere alignment is needed.
  77. ///
  78. /// NOTE: Do not change this value per-platform unless you have a very good
  79. /// reason for doing so. It has the potential to cause inconsistencies in
  80. /// memory which is allocated and expected to be contiguous.
  81. #define TORQUE_BYTE_ALIGNMENT 4
  82. void* FrameAllocator::alloc(const U32 allocSize)
  83. {
  84. U32 _allocSize = allocSize;
  85. #ifdef TORQUE_DEBUG
  86. _allocSize+=4;
  87. #endif
  88. AssertFatal(smBuffer != NULL, "Error, no buffer!");
  89. AssertFatal(smWaterMark + _allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!");
  90. // Keep all frame allocator allocations aligned to DWORD boundries on the 360
  91. // Add 3, mask out the lower 3 bits.
  92. smWaterMark = ( smWaterMark + ( TORQUE_BYTE_ALIGNMENT - 1 ) ) & (~( TORQUE_BYTE_ALIGNMENT - 1 ));
  93. // Sanity check.
  94. AssertFatal( !( smWaterMark & ( TORQUE_BYTE_ALIGNMENT - 1 ) ), "Frame allocation is not on a 4-byte boundry." );
  95. U8* p = &smBuffer[smWaterMark];
  96. smWaterMark += _allocSize;
  97. #if defined(TORQUE_DEBUG)
  98. if (smWaterMark > sgMaxFrameAllocation)
  99. sgMaxFrameAllocation = smWaterMark;
  100. #endif
  101. #ifdef TORQUE_DEBUG
  102. U32 *flag = (U32*) &smBuffer[smWaterMark-4];
  103. *flag = 0xdeadbeef ^ smWaterMark;
  104. #endif
  105. return p;
  106. }
  107. void FrameAllocator::setWaterMark(const U32 waterMark)
  108. {
  109. AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark");
  110. #ifdef TORQUE_DEBUG
  111. if(smWaterMark >= 4 )
  112. {
  113. U32 *flag = (U32*) &smBuffer[smWaterMark-4];
  114. AssertFatal( *flag == (0xdeadbeef ^ smWaterMark), "FrameAllocator guard overwritten!");
  115. }
  116. #endif
  117. smWaterMark = waterMark;
  118. }
  119. U32 FrameAllocator::getWaterMark()
  120. {
  121. return smWaterMark;
  122. }
  123. U32 FrameAllocator::getHighWaterMark()
  124. {
  125. return smHighWaterMark;
  126. }
  127. /// Helper class to deal with FrameAllocator usage.
  128. ///
  129. /// The purpose of this class is to make it simpler and more reliable to use the
  130. /// FrameAllocator. Simply use it like this:
  131. ///
  132. /// @code
  133. /// FrameAllocatorMarker mem;
  134. ///
  135. /// char *buff = (char*)mem.alloc(100);
  136. /// @endcode
  137. ///
  138. /// When you leave the scope you defined the FrameAllocatorMarker in, it will
  139. /// automatically restore the watermark on the FrameAllocator. In situations
  140. /// with complex branches, this can be a significant headache remover, as you
  141. /// don't have to remember to reset the FrameAllocator on every posssible branch.
  142. class FrameAllocatorMarker
  143. {
  144. U32 mMarker;
  145. public:
  146. FrameAllocatorMarker()
  147. {
  148. mMarker = FrameAllocator::getWaterMark();
  149. }
  150. ~FrameAllocatorMarker()
  151. {
  152. FrameAllocator::setWaterMark(mMarker);
  153. }
  154. void* alloc(const U32 allocSize) const
  155. {
  156. return FrameAllocator::alloc(allocSize);
  157. }
  158. };
  159. /// Class for temporary variables that you want to allocate easily using
  160. /// the FrameAllocator. For example:
  161. /// @code
  162. /// FrameTemp<char> tempStr(32); // NOTE! This parameter is NOT THE SIZE IN BYTES. See constructor docs.
  163. /// dStrcat( tempStr, SomeOtherString );
  164. /// tempStr[2] = 'l';
  165. /// Con::printf( tempStr );
  166. /// Con::printf( "Foo: %s", ~tempStr );
  167. /// @endcode
  168. ///
  169. /// This will automatically handle getting and restoring the watermark of the
  170. /// FrameAllocator when it goes out of scope. You should notice the strange
  171. /// operator infront of tempStr on the printf call. This is normally a unary
  172. /// operator for ones-complement, but in this class it will simply return the
  173. /// memory of the allocation. It's the same as doing (const char *)tempStr
  174. /// in the above case. The reason why it is necessary for the second printf
  175. /// and not the first is because the second one is taking a variable arg
  176. /// list and so it isn't getting the cast so that it's cast operator can
  177. /// properly return the memory instead of the FrameTemp object itself.
  178. ///
  179. /// @note It is important to note that this object is designed to just be a
  180. /// temporary array of a dynamic size. Some wierdness may occur if you try
  181. /// do perform crazy pointer stuff with it using regular operators on it.
  182. /// I implemented what I thought were the most common operators that it
  183. /// would be used for. If strange things happen, you will need to debug
  184. /// them yourself.
  185. template<class T>
  186. class FrameTemp
  187. {
  188. protected:
  189. U32 mWaterMark;
  190. T *mMemory;
  191. U32 mNumObjectsInMemory;
  192. public:
  193. /// Constructor will store the FrameAllocator watermark and allocate the memory off
  194. /// of the FrameAllocator.
  195. ///
  196. /// @note It is important to note that, unlike the FrameAllocatorMarker and the
  197. /// FrameAllocator itself, the argument to allocate is NOT the size in bytes,
  198. /// doing:
  199. /// @code
  200. /// FrameTemp<F64> f64s(5);
  201. /// @endcode
  202. /// Is the same as
  203. /// @code
  204. /// F64 *f64s = new F64[5];
  205. /// @endcode
  206. ///
  207. /// @param count The number of objects to allocate
  208. FrameTemp( const U32 count = 1 ) : mNumObjectsInMemory( count )
  209. {
  210. AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" );
  211. mWaterMark = FrameAllocator::getWaterMark();
  212. mMemory = reinterpret_cast<T *>( FrameAllocator::alloc( sizeof( T ) * count ) );
  213. for( U32 i = 0; i < mNumObjectsInMemory; i++ )
  214. constructInPlace<T>( &mMemory[i] );
  215. }
  216. /// Destructor restores the watermark
  217. ~FrameTemp()
  218. {
  219. // Call destructor
  220. for( U32 i = 0; i < mNumObjectsInMemory; i++ )
  221. destructInPlace<T>( &mMemory[i] );
  222. FrameAllocator::setWaterMark( mWaterMark );
  223. }
  224. U32 getObjectCount( void ) const { return mNumObjectsInMemory; }
  225. /// NOTE: This will return the memory, NOT perform a ones-complement
  226. T* operator ~() { return mMemory; };
  227. /// NOTE: This will return the memory, NOT perform a ones-complement
  228. const T* operator ~() const { return mMemory; };
  229. /// NOTE: This will dereference the memory, NOT do standard unary plus behavior
  230. T& operator +() { return *mMemory; };
  231. /// NOTE: This will dereference the memory, NOT do standard unary plus behavior
  232. const T& operator +() const { return *mMemory; };
  233. T& operator *() { return *mMemory; };
  234. const T& operator *() const { return *mMemory; };
  235. T** operator &() { return &mMemory; };
  236. const T** operator &() const { return &mMemory; };
  237. operator T*() { return mMemory; }
  238. operator const T*() const { return mMemory; }
  239. operator T&() { return *mMemory; }
  240. operator const T&() const { return *mMemory; }
  241. operator T() { return *mMemory; }
  242. operator const T() const { return *mMemory; }
  243. // This ifdef is to satisfy the ever so pedantic GCC compiler
  244. // Which seems to upset visual studio.
  245. T& operator[]( const U32 idx ) { return mMemory[idx]; }
  246. const T& operator[]( const U32 idx ) const { return mMemory[idx]; }
  247. T& operator[]( const S32 idx ) { return mMemory[idx]; }
  248. const T& operator[]( const S32 idx ) const { return mMemory[idx]; }
  249. };
  250. //-----------------------------------------------------------------------------
  251. // FrameTemp specializations for types with no constructor/destructor
  252. #define FRAME_TEMP_NC_SPEC(type) \
  253. template<> \
  254. inline FrameTemp<type>::FrameTemp( const U32 count ) \
  255. { \
  256. AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \
  257. mWaterMark = FrameAllocator::getWaterMark(); \
  258. mMemory = reinterpret_cast<type *>( FrameAllocator::alloc( sizeof( type ) * count ) ); \
  259. } \
  260. template<>\
  261. inline FrameTemp<type>::~FrameTemp() \
  262. { \
  263. FrameAllocator::setWaterMark( mWaterMark ); \
  264. } \
  265. FRAME_TEMP_NC_SPEC(char);
  266. FRAME_TEMP_NC_SPEC(float);
  267. FRAME_TEMP_NC_SPEC(double);
  268. FRAME_TEMP_NC_SPEC(bool);
  269. FRAME_TEMP_NC_SPEC(int);
  270. FRAME_TEMP_NC_SPEC(short);
  271. FRAME_TEMP_NC_SPEC(unsigned char);
  272. FRAME_TEMP_NC_SPEC(unsigned int);
  273. FRAME_TEMP_NC_SPEC(unsigned short);
  274. #undef FRAME_TEMP_NC_SPEC
  275. //-----------------------------------------------------------------------------
  276. #endif // _H_FRAMEALLOCATOR_