Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

995 lines
38 KiB

  1. //================ Copyright (c) Valve Corporation. All Rights Reserved. ===========================
  2. //
  3. // GCM memory allocation mgmt
  4. //
  5. //==================================================================================================
  6. #include "utlmap.h"
  7. #include "sys/tty.h"
  8. #include "convar.h"
  9. #include "ps3gcmmemory.h"
  10. #include "gcmlabels.h"
  11. #include "gcmstate.h"
  12. #include "gcmdrawstate.h"
  13. #include "memdbgon.h"
  14. PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL
  15. #define HARDWARE_CURSOR_SIZE (64*64*4)
  16. //--------------------------------------------------------------------------------------------------
  17. // GCM memory allocators
  18. //--------------------------------------------------------------------------------------------------
  19. #if 1 // #ifndef _CERT
  20. #define TRACK_ALLOC_STATS 1
  21. #endif
  22. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  23. ConVar r_ps3_gcmnocompact( "r_ps3_gcmnocompact", "0" );
  24. ConVar r_ps3_gcmlowcompact( "r_ps3_gcmlowcompact", "1" );
  25. #endif
  26. static CThreadFastMutex s_AllocMutex;
  27. static int32 s_uiGcmLocalMemoryAllocatorMutexLockCount;
  28. struct CGcmLocalMemoryAllocatorMutexLockCounter_t
  29. {
  30. CGcmLocalMemoryAllocatorMutexLockCounter_t() { Assert( s_uiGcmLocalMemoryAllocatorMutexLockCount >= 0 ); ++ s_uiGcmLocalMemoryAllocatorMutexLockCount; }
  31. ~CGcmLocalMemoryAllocatorMutexLockCounter_t() { Assert( s_uiGcmLocalMemoryAllocatorMutexLockCount > 0 ); -- s_uiGcmLocalMemoryAllocatorMutexLockCount; }
  32. };
  33. #define PS3ALLOCMTX AUTO_LOCK( s_AllocMutex ); CGcmLocalMemoryAllocatorMutexLockCounter_t aLockCounter;
  34. bool IsItSafeToRefreshFrontBufferNonInteractivePs3()
  35. {
  36. // NOTE: only main thread can refresh front buffer
  37. if ( !ThreadInMainThread() )
  38. return false;
  39. AUTO_LOCK( s_AllocMutex );
  40. Assert( s_uiGcmLocalMemoryAllocatorMutexLockCount >= 0 );
  41. return s_uiGcmLocalMemoryAllocatorMutexLockCount <= 0;
  42. }
  43. struct CPs3gcmLocalMemoryBlockMutable : public CPs3gcmLocalMemoryBlock
  44. {
  45. inline uint32 & MutableOffset() { return m_nLocalMemoryOffset; }
  46. inline uint32 & MutableSize() { return m_uiSize; }
  47. inline CPs3gcmAllocationType_t & MutableType() { return m_uType; }
  48. inline uint32 & MutableIndex() { return m_uiIndex; }
  49. };
  50. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  51. static const uint64 g_GcmLocalMemoryBlockDebugCookieAllocated = 0xA110CA7EDA110CA7ull;
  52. static const uint64 g_GcmLocalMemoryBlockDebugCookieFree = 0xFEEFEEFEEFEEFEEFllu;
  53. #endif
  54. struct CPs3gcmLocalMemoryAllocator
  55. {
  56. //////////////////////////////////////////////////////////////////////////
  57. //
  58. // Allocated memory tracking
  59. //
  60. uint32 m_nOffsetMin; // RSX Local Memory allocated by Initialization that will never be released
  61. uint32 m_nOffsetMax; // Ceiling of allocatable RSX Local Memory (because the top portion is reserved for zcull/etc.), top portion managed separately
  62. uint32 m_nOffsetUnallocated; // RSX Local Memory offset of not yet allocated memory (between Min and Max)
  63. CUtlVector< CPs3gcmLocalMemoryBlockMutable * > m_arrAllocations; // Sorted array of all allocations
  64. //////////////////////////////////////////////////////////////////////////
  65. //
  66. // Free blocks tracking
  67. //
  68. struct LocalMemoryAllocation_t
  69. {
  70. CPs3gcmLocalMemoryBlockMutable m_block;
  71. uint32 m_uiFenceNumber;
  72. LocalMemoryAllocation_t *m_pNext;
  73. };
  74. LocalMemoryAllocation_t *m_pPendingFreeBlock;
  75. LocalMemoryAllocation_t *m_pFreeBlock;
  76. static uint32 sm_uiFenceNumber;
  77. uint32 m_uiFenceLastKnown;
  78. static uint32 volatile *sm_puiFenceLocation;
  79. //////////////////////////////////////////////////////////////////////////
  80. //
  81. // Implementation
  82. //
  83. inline bool Alloc( CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock );
  84. inline void Free( CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock );
  85. inline uint32 Reclaim( bool bForce = false );
  86. inline void Compact();
  87. // Helper methods
  88. inline LocalMemoryAllocation_t * FindFreeBlock( uint32 uiAlignBytes, uint32 uiSize );
  89. inline bool IsFenceCompleted( uint32 uiCurrentFenceValue, uint32 uiCheckStoredFenceValue );
  90. inline void TrackAllocStats( CPs3gcmAllocationType_t uAllocType, int nDelta );
  91. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  92. inline void ValidateAllBlocks();
  93. #endif
  94. }
  95. g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolCount];
  96. uint32 CPs3gcmLocalMemoryAllocator::sm_uiFenceNumber = 1;
  97. uint32 volatile * CPs3gcmLocalMemoryAllocator::sm_puiFenceLocation;
  98. // RSX memory usage stats tracking:
  99. static GPUMemoryStats g_RsxMemoryStats;
  100. struct GPUMemoryStats_Pool
  101. {
  102. int nDefaultPoolSize;
  103. int nDefaultPoolUsed;
  104. int nRTPoolUsed;
  105. int nDynamicPoolUsed;
  106. int nMainMemUsed;
  107. int nUnknownPoolUsed;
  108. };
  109. GPUMemoryStats_Pool g_RsxMemoryStats_Pool;
  110. static inline uint32 Ps3gcmHelper_ComputeTiledAreaMemorySize( uint32 nCount, uint32 w, uint32 h, uint32 bpp )
  111. {
  112. uint32 nTilePitch = cellGcmGetTiledPitchSize( w * bpp );
  113. uint32 uiSize = nTilePitch * AlignValue( h, 32 );
  114. uiSize *= nCount;
  115. uiSize = AlignValue( uiSize, PS3GCMALLOCATIONALIGN( kAllocPs3gcmColorBufferMisc ) );
  116. return uiSize;
  117. }
  118. void Ps3gcmLocalMemoryAllocator_Init()
  119. {
  120. PS3ALLOCMTX
  121. if ( !CPs3gcmLocalMemoryAllocator::sm_puiFenceLocation )
  122. {
  123. CPs3gcmLocalMemoryAllocator::sm_puiFenceLocation = cellGcmGetLabelAddress( GCM_LABEL_MEMORY_FREE );
  124. *CPs3gcmLocalMemoryAllocator::sm_puiFenceLocation = 0;
  125. }
  126. // Pool boundaries
  127. uint32 uiGcmAllocBegin = g_ps3gcmGlobalState.m_nLocalBaseOffset;
  128. uint32 uiGcmAllocEnd = uiGcmAllocBegin + g_ps3gcmGlobalState.m_nLocalSize;
  129. // Memory should be allocated for large frame buffers
  130. uint32 uiMemorySizeBuffer[2] = { MAX( 1280, g_ps3gcmGlobalState.m_nRenderSize[0] ), MAX( 720, g_ps3gcmGlobalState.m_nRenderSize[1] ) };
  131. uint32 uiFactor[2] = { uiMemorySizeBuffer[0]*uiMemorySizeBuffer[1], 1280*720 };
  132. // Configuration of pool memory (can be #ifdef'd for every game)
  133. static const uint32 s_PoolMemoryLayout[/*kGcmAllocPoolCount*/] =
  134. {
  135. #if defined( CSTRIKE15 )
  136. // mhansen - We had to adjust the memory values a bit for cstrike15 to get a map to load
  137. // PS3_BUILDFIX - We need to revisit this to determine the proper size later on
  138. /*kGcmAllocPoolDefault = */ 0,
  139. /*kGcmAllocPoolDynamicNewPath = */ 6 * 1024 * 1024, // 5 MB
  140. /*kGcmAllocPoolDynamic = */ 11 * 1024 * 1024, // 11 MB
  141. /*kGcmAllocPoolTiledColorFB = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 2 + CPs3gcmDisplay::SURFACE_COUNT, uiMemorySizeBuffer[0], uiMemorySizeBuffer[1], 4 ),
  142. // 2 buffers allocated in CreateRSXBuffers + 2 _rt_fullFrameFB - can probably get this down if...
  143. // 1. we clean up the post-pro rendering to use the front buffer as a textureand
  144. // 2. tidy up aliasing for rt_fullframeFB and rt_fullFrameFB1
  145. /*kGcmAllocPoolTiledColorFBQ = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 2, uiMemorySizeBuffer[0]/4, uiMemorySizeBuffer[1]/4, 4 ), // fits 2 1/4 size framebuffer textures
  146. /*kGcmAllocPoolTiledColor512 = */ 0,
  147. /*kGcmAllocPoolTiledColorMisc = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 1, 640, 640, 4 ) // RTT shadows ?
  148. + Ps3gcmHelper_ComputeTiledAreaMemorySize( 2, 1024, 512, 4) // Water
  149. + Ps3gcmHelper_ComputeTiledAreaMemorySize(1, 32, 32, 4), // Eye Glint
  150. /*kGcmAllocPoolTiledD24S8 = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 1, 640*2, 640*2, 2)
  151. + Ps3gcmHelper_ComputeTiledAreaMemorySize(1, 640, 640, 2) // CSM and Flashlight
  152. + Ps3gcmHelper_ComputeTiledAreaMemorySize( 1, uiMemorySizeBuffer[0], uiMemorySizeBuffer[1], 4 ), // Main depth buffer
  153. /*kGcmAllocPoolMainMemory = */ 0, // configured based on mapped IO memory
  154. /*kGcmAllocPoolMallocMemory = */ 0, // using malloc
  155. #else
  156. /*kGcmAllocPoolDefault = */ 0,
  157. /*kGcmAllocPoolDynamicNewPath = */ 5 * 1024 * 1024, // 5 MB
  158. /*kGcmAllocPoolDynamic = */ 10 * 1024 * 1024, // 10 MB
  159. /*kGcmAllocPoolTiledColorFB = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 2 * CPs3gcmDisplay::SURFACE_COUNT, uiMemorySizeBuffer[0], uiMemorySizeBuffer[1], 4 ), // fits 6 of full framebuffer textures
  160. /*kGcmAllocPoolTiledColorFBQ = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 4, uiMemorySizeBuffer[0]/4, uiMemorySizeBuffer[1]/4, 4 ), // fits 4 quarters of framebuffer textures
  161. /*kGcmAllocPoolTiledColor512 = */ Ps3gcmHelper_ComputeTiledAreaMemorySize( 2, 512, 512, 4 ), // fits 2 512x512 RGBA textures
  162. /*kGcmAllocPoolTiledColorMisc = */ 5 * 1024 * 1024, // 5 MB
  163. /*kGcmAllocPoolTiledD24S8 = */ uint64( 15 * 1024 * 1024 ) * uiFactor[0]/uiFactor[1], // 15 MB
  164. /*kGcmAllocPoolMainMemory = */ 0, // configured based on mapped IO memory
  165. /*kGcmAllocPoolMallocMemory = */ 0, // using malloc
  166. #endif
  167. };
  168. COMPILE_TIME_ASSERT( ARRAYSIZE( s_PoolMemoryLayout ) == ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ) );
  169. for ( int j = ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ); j -- > 0; )
  170. {
  171. const uint32 uiSize = AlignValue( s_PoolMemoryLayout[j], 1024 * 1024 ); // Align it on 1 MB boundaries, all our pools are large
  172. g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMax = uiGcmAllocEnd;
  173. uiGcmAllocEnd -= uiSize;
  174. g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMin =
  175. g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetUnallocated = uiGcmAllocEnd;
  176. }
  177. // Default pool setup (rest of local memory)
  178. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ].m_nOffsetMax = uiGcmAllocEnd;
  179. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ].m_nOffsetMin =
  180. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ].m_nOffsetUnallocated = uiGcmAllocBegin + HARDWARE_CURSOR_SIZE;
  181. // Main memory mapped pool
  182. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ].m_nOffsetMin =
  183. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ].m_nOffsetUnallocated = uint32( g_ps3gcmGlobalState.m_pRsxMainMemoryPoolBuffer ) + g_ps3gcmGlobalState.m_nIoOffsetDelta;
  184. g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ].m_nOffsetMax = g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ].m_nOffsetMin + g_ps3gcmGlobalState.m_nRsxMainMemoryPoolBufferSize;
  185. // Store initial capacity for memory stats tracking:
  186. g_RsxMemoryStats.nGPUMemSize = g_ps3gcmGlobalState.m_nLocalSize;
  187. g_RsxMemoryStats_Pool.nDefaultPoolSize = g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ].m_nOffsetMin;
  188. //
  189. // Setup preset tiled regions
  190. //
  191. {
  192. CPs3gcmAllocationPool_t ePool = kGcmAllocPoolTiledColorFB;
  193. uint8 uiBank = 0; // bank 0..3
  194. uint32 nRenderPitch = cellGcmGetTiledPitchSize( g_ps3gcmGlobalState.m_nRenderSize[0] * 4 );
  195. uint8 uiTileIndex = ePool - kGcmAllocPoolTiledColorFB;
  196. cellGcmSetTileInfo( uiTileIndex, CELL_GCM_LOCATION_LOCAL,
  197. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  198. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  199. nRenderPitch, CELL_GCM_COMPMODE_DISABLED,
  200. ( g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolTiledColorFB ].m_nOffsetMin ) / 0x10000, // The area base + size/0x10000 will be allocated as the tag area.
  201. uiBank );
  202. cellGcmBindTile( uiTileIndex );
  203. }
  204. {
  205. CPs3gcmAllocationPool_t ePool = kGcmAllocPoolTiledColorFBQ;
  206. uint8 uiBank = 1; // bank 0..3
  207. uint32 nRenderPitch = cellGcmGetTiledPitchSize( g_ps3gcmGlobalState.m_nRenderSize[0] * 4 / 4 );
  208. uint8 uiTileIndex = ePool - kGcmAllocPoolTiledColorFB;
  209. cellGcmSetTileInfo( uiTileIndex, CELL_GCM_LOCATION_LOCAL,
  210. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  211. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  212. nRenderPitch, CELL_GCM_COMPMODE_DISABLED,
  213. ( g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolTiledColorFB ].m_nOffsetMin ) / 0x10000, // The area base + size/0x10000 will be allocated as the tag area.
  214. uiBank );
  215. cellGcmBindTile( uiTileIndex );
  216. }
  217. {
  218. CPs3gcmAllocationPool_t ePool = kGcmAllocPoolTiledColor512;
  219. uint8 uiBank = 2; // bank 0..3
  220. uint32 nRenderPitch = cellGcmGetTiledPitchSize( 512 * 4 );
  221. uint8 uiTileIndex = ePool - kGcmAllocPoolTiledColorFB;
  222. cellGcmSetTileInfo( uiTileIndex, CELL_GCM_LOCATION_LOCAL,
  223. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  224. g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin,
  225. nRenderPitch, CELL_GCM_COMPMODE_DISABLED,
  226. ( g_ps3gcmLocalMemoryAllocator[ ePool ].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolTiledColorFB ].m_nOffsetMin ) / 0x10000, // The area base + size/0x10000 will be allocated as the tag area.
  227. uiBank );
  228. cellGcmBindTile( uiTileIndex );
  229. }
  230. #ifndef _CERT
  231. static const char * s_PoolMemoryNames[] =
  232. {
  233. /*kGcmAllocPoolDefault = */ "Default Pool",
  234. /*kGcmAllocPoolDynamicNewPath = */ "Dynamic New ",
  235. /*kGcmAllocPoolDynamic = */ "Dynamic IBVB",
  236. /*kGcmAllocPoolTiledColorFB = */ "FullFrameRTs",
  237. /*kGcmAllocPoolTiledColorFBQ = */ "1/4Frame RTs",
  238. /*kGcmAllocPoolTiledColor512 = */ "512x512 RTs ",
  239. /*kGcmAllocPoolTiledColorMisc = */ "All Misc RTs",
  240. /*kGcmAllocPoolTiledD24S8 = */ "DepthStencil",
  241. /*kGcmAllocPoolMainMemory = */ "Main Memory ",
  242. /*kGcmAllocPoolMallocMemory = */ "MallocMemory",
  243. };
  244. COMPILE_TIME_ASSERT( ARRAYSIZE( s_PoolMemoryNames ) == ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ) );
  245. Msg( "RSX Local Memory layout:\n" );
  246. for ( int j = 0; j < ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ); ++ j )
  247. {
  248. Msg( " %s 0x%08X - 0x%08X [ %9.3f MB ]\n",
  249. s_PoolMemoryNames[j],
  250. g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMin,
  251. g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMax,
  252. (g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[ j ].m_nOffsetMin) / 1024.f / 1024.f );
  253. }
  254. Msg( "Total size: %d MB\n", g_ps3gcmGlobalState.m_nLocalSize / 1024 / 1024 );
  255. #endif
  256. }
  257. void Ps3gcmLocalMemoryAllocator_Reclaim()
  258. {
  259. PS3ALLOCMTX
  260. for ( int k = 0; k < ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ); ++ k )
  261. g_ps3gcmLocalMemoryAllocator[ k ].Reclaim();
  262. }
  263. void Ps3gcmLocalMemoryAllocator_Compact()
  264. {
  265. PS3ALLOCMTX
  266. #define PS3GCMCOMPACTPROFILE 0
  267. #if PS3GCMCOMPACTPROFILE
  268. float flTimeStart = Plat_FloatTime();
  269. uint32 uiFree = g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated;
  270. #endif
  271. // Let PPU wait for all RSX commands done (include waitFlip)
  272. // Flush GPU right up to current point - Endframe call does this...
  273. gpGcmDrawState->EndFrame();
  274. gpGcmDrawState->CmdBufferFinish();
  275. #if PS3GCMCOMPACTPROFILE
  276. float flTimeWait = Plat_FloatTime() - flTimeStart;
  277. #endif
  278. {
  279. // Locking out memory mgmt for the whole of the compact before this
  280. // PS3ALLOCMTX
  281. for ( int k = 0; k < ARRAYSIZE( g_ps3gcmLocalMemoryAllocator ); ++ k )
  282. {
  283. g_ps3gcmLocalMemoryAllocator[ k ].Compact();
  284. }
  285. }
  286. #if PS3GCMCOMPACTPROFILE
  287. float flTimePrepareTransfer = Plat_FloatTime() - flTimeStart;
  288. #endif
  289. // Wait for all RSX memory to be transferred
  290. gpGcmDrawState->EndFrame();
  291. gpGcmDrawState->CmdBufferFinish();
  292. #if PS3GCMCOMPACTPROFILE
  293. float flTimeDone = Plat_FloatTime() - flTimeStart;
  294. char chBuffer[64];
  295. Q_snprintf( chBuffer, ARRAYSIZE( chBuffer ), "COMPACT: %0.3f / %0.3f / %0.3f sec\n",
  296. flTimeWait, flTimePrepareTransfer, flTimeDone );
  297. uint32 dummy;
  298. sys_tty_write( SYS_TTYP6, chBuffer, Q_strlen( chBuffer ), &dummy );
  299. Q_snprintf( chBuffer, ARRAYSIZE( chBuffer ), "COMPACT: %0.3f -> %0.3f MB (%0.3f MB free)\n",
  300. uiFree / 1024.f / 1024.f, g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated / 1024.f / 1024.f,
  301. (g_ps3gcmLocalMemoryAllocator[0].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated) / 1024.f / 1024.f );
  302. sys_tty_write( SYS_TTYP6, chBuffer, Q_strlen( chBuffer ), &dummy );
  303. #endif
  304. }
  305. void Ps3gcmLocalMemoryAllocator_CompactWithReason( char const *szReason )
  306. {
  307. double flTimeCompactStart = Plat_FloatTime();
  308. DevMsg( "====== GCM LOCAL MEMORY COMPACT : %s =====\n", szReason );
  309. uint32 uiFreeMemoryBeforeCompact = g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated;
  310. DevMsg( "RSX Local Memory Free: %0.3f MB; compacting...\n", (g_ps3gcmLocalMemoryAllocator[0].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated) / 1024.f / 1024.f );
  311. Ps3gcmLocalMemoryAllocator_Compact();
  312. DevMsg( "RSX Local Memory Compacted %0.3f MB in %0.3f sec\n",
  313. (uiFreeMemoryBeforeCompact - g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated) / 1024.f / 1024.f,
  314. Plat_FloatTime() - flTimeCompactStart );
  315. DevMsg( "RSX Local Memory Free: %0.3f MB\n", (g_ps3gcmLocalMemoryAllocator[0].m_nOffsetMax - g_ps3gcmLocalMemoryAllocator[0].m_nOffsetUnallocated) / 1024.f / 1024.f );
  316. }
  317. bool CPs3gcmLocalMemoryBlock::Alloc()
  318. {
  319. PS3ALLOCMTX
  320. return g_ps3gcmLocalMemoryAllocator[PS3GCMALLOCATIONPOOL(m_uType)].Alloc( reinterpret_cast< CPs3gcmLocalMemoryBlockMutable * >( this ) );
  321. }
  322. void CPs3gcmLocalMemoryBlock::Free()
  323. {
  324. PS3ALLOCMTX
  325. g_ps3gcmLocalMemoryAllocator[PS3GCMALLOCATIONPOOL(m_uType)].Free( reinterpret_cast< CPs3gcmLocalMemoryBlockMutable * >( this ) );
  326. }
  327. //////////////////////////////////////////////////////////////////////////
  328. //
  329. // Private implementation of PS3 local memory allocator
  330. //
  331. inline bool CPs3gcmLocalMemoryAllocator::Alloc( CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock )
  332. {
  333. TrackAllocStats( pBlock->MutableType(), pBlock->MutableSize() );
  334. uint32 uAlignBytes = PS3GCMALLOCATIONALIGN( pBlock->MutableType() );
  335. Assert( IsPowerOfTwo( uAlignBytes ) );
  336. double flAllocatorStallTime = 0.0f;
  337. bool bCompactPerformed = false;
  338. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  339. bCompactPerformed = !r_ps3_gcmlowcompact.GetBool();
  340. #endif
  341. retry_allocation:
  342. // Try to find a free block
  343. if ( LocalMemoryAllocation_t *pFreeBlock = FindFreeBlock( uAlignBytes, pBlock->MutableSize() ) )
  344. {
  345. pBlock->MutableOffset() = pFreeBlock->m_block.MutableOffset();
  346. pBlock->MutableIndex() = pFreeBlock->m_block.MutableIndex();
  347. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  348. if ( m_arrAllocations[ pBlock->MutableIndex() ] != &pFreeBlock->m_block )
  349. Error( "<vitaliy> GCM Local Memory Allocator Error (attempt to reuse invalid free block)!" );
  350. #endif
  351. m_arrAllocations[ pBlock->MutableIndex() ] = reinterpret_cast< CPs3gcmLocalMemoryBlockMutable * >( pBlock );
  352. delete pFreeBlock;
  353. }
  354. else if ( this != &g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMallocMemory ] )
  355. {
  356. // Allocate new block
  357. uint32 uiOldUnallocatedEdge = m_nOffsetUnallocated;
  358. uint32 uiFreeBlock = ( m_nOffsetUnallocated + uAlignBytes - 1 ) & ~( uAlignBytes - 1 );
  359. // Check if there's enough space in this pool for the requested block
  360. if ( uiFreeBlock + pBlock->MutableSize() > m_nOffsetMax )
  361. {
  362. // There's not enough space in this pool
  363. if ( m_pPendingFreeBlock )
  364. {
  365. // There are pending free blocks, we just need to wait for
  366. // RSX to finish rendering using them
  367. if ( !flAllocatorStallTime )
  368. {
  369. flAllocatorStallTime = Plat_FloatTime();
  370. // Flush GPU right up to current point - Endframe call does this...
  371. gpGcmDrawState->EndFrame();
  372. gpGcmDrawState->CmdBufferFlush();
  373. }
  374. while ( Reclaim() < pBlock->MutableSize() && m_pPendingFreeBlock )
  375. {
  376. ThreadSleep( 1 );
  377. }
  378. goto retry_allocation;
  379. }
  380. else if ( !bCompactPerformed )
  381. {
  382. if (this == &g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ]) return false;
  383. // Let PPU wait for all RSX commands done
  384. gpGcmDrawState->EndFrame();
  385. gpGcmDrawState->CmdBufferFinish();
  386. uint32 uiFragmentedFreeSpace = m_nOffsetMax - m_nOffsetUnallocated;
  387. for ( LocalMemoryAllocation_t *pFreeFragment = m_pFreeBlock; pFreeFragment; pFreeFragment = pFreeFragment->m_pNext )
  388. uiFragmentedFreeSpace += pFreeFragment->m_block.MutableSize();
  389. Warning(
  390. "**************** GCM LOCAL MEMORY LOW *****************\n"
  391. "<vitaliy> GCM Local Memory Allocator#%d pool compacting!\n"
  392. " Requested allocation %u bytes.\n"
  393. " Pool capacity %u bytes.\n"
  394. " Free fragmented space %u bytes.\n"
  395. " Unallocated %u bytes.\n"
  396. " Used %u bytes.\n",
  397. this - g_ps3gcmLocalMemoryAllocator,
  398. ( uint32 ) pBlock->MutableSize(),
  399. m_nOffsetMax - m_nOffsetMin,
  400. uiFragmentedFreeSpace,
  401. m_nOffsetMax - m_nOffsetUnallocated,
  402. m_nOffsetUnallocated - m_nOffsetMin
  403. );
  404. Compact();
  405. Warning( " ---> Compacted pool#%d has %u unallocated bytes.\n",
  406. this - g_ps3gcmLocalMemoryAllocator,
  407. m_nOffsetMax - m_nOffsetUnallocated );
  408. bCompactPerformed = true;
  409. // Wait for all RSX memory to be transferred
  410. gpGcmDrawState->EndFrame();
  411. gpGcmDrawState->CmdBufferFinish();
  412. goto retry_allocation;
  413. }
  414. else
  415. {
  416. // Main memory pool returns failure so caller can try local pool.
  417. if (this == &g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMainMemory ]) return false;
  418. uint32 uiFragmentedFreeSpace = m_nOffsetMax - m_nOffsetUnallocated;
  419. for ( LocalMemoryAllocation_t *pFreeFragment = m_pFreeBlock; pFreeFragment; pFreeFragment = pFreeFragment->m_pNext )
  420. uiFragmentedFreeSpace += pFreeFragment->m_block.MutableSize();
  421. Error(
  422. "********* OUT OF GCM LOCAL MEMORY ********************\n"
  423. "<vitaliy> GCM Local Memory Allocator#%d pool exhausted!\n"
  424. " Failed allocation %u bytes.\n"
  425. " Pool capacity %u bytes.\n"
  426. " Free fragmented space %u bytes.\n"
  427. " Unallocated %u bytes.\n"
  428. " Used %u bytes.\n",
  429. this - g_ps3gcmLocalMemoryAllocator,
  430. ( uint32 ) pBlock->MutableSize(),
  431. m_nOffsetMax - m_nOffsetMin,
  432. uiFragmentedFreeSpace,
  433. m_nOffsetMax - m_nOffsetUnallocated,
  434. m_nOffsetUnallocated - m_nOffsetMin
  435. );
  436. }
  437. }
  438. // update the pointer to "unallocated" realm
  439. m_nOffsetUnallocated = uiFreeBlock + pBlock->MutableSize();
  440. // this is the last allocation so far
  441. pBlock->MutableIndex() = m_arrAllocations.AddToTail( reinterpret_cast< CPs3gcmLocalMemoryBlockMutable * >( pBlock ) );
  442. pBlock->MutableOffset() = uiFreeBlock;
  443. }
  444. else
  445. {
  446. MEM_ALLOC_CREDIT_( "GCM Malloc Pool" );
  447. void *pvMallocMemory = MemAlloc_AllocAligned( pBlock->MutableSize(), uAlignBytes );
  448. pBlock->MutableOffset() = (uint32) pvMallocMemory;
  449. pBlock->MutableIndex() = ~0;
  450. }
  451. // 7LTODO if ( flAllocatorStallTime )
  452. // g_ps3gcmGlobalState.m_flAllocatorStallTimeWaitingRSX += Plat_FloatTime() - flAllocatorStallTime;
  453. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  454. // PS3 doesn't allow more than 8 zcull regions (index 0..7)
  455. if ( g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledD24S8].m_arrAllocations.Count() > 8 )
  456. Error( "PS3 number of zcull regions exceeded!\n" );
  457. // PS3 doesn't allow more than 15 tiles regions (index 0..14)
  458. if ( g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledD24S8].m_arrAllocations.Count() +
  459. g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorMisc].m_arrAllocations.Count() +
  460. ( kGcmAllocPoolTiledColorMisc - kGcmAllocPoolTiledColorFB )
  461. > 15 )
  462. Error( "PS3 number of tiled regions exceeded!\n" );
  463. pBlock->m_dbgGuardCookie = g_GcmLocalMemoryBlockDebugCookieAllocated;
  464. #endif
  465. return true;
  466. }
  467. inline void CPs3gcmLocalMemoryAllocator::Free( CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock )
  468. {
  469. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  470. if ( !pBlock ||
  471. pBlock->m_dbgGuardCookie != g_GcmLocalMemoryBlockDebugCookieAllocated ||
  472. ( ( pBlock->MutableIndex() != ~0 ) && ( m_arrAllocations[ pBlock->MutableIndex() ] != pBlock ) ) )
  473. {
  474. //DebuggerBreak();
  475. Error( "<vitaliy> Attempt to free not allocated GCM local memory block!" );
  476. }
  477. pBlock->m_dbgGuardCookie = g_GcmLocalMemoryBlockDebugCookieFree;
  478. #endif
  479. LocalMemoryAllocation_t *pDealloc = new LocalMemoryAllocation_t;
  480. pDealloc->m_block = *pBlock;
  481. pDealloc->m_uiFenceNumber = sm_uiFenceNumber;
  482. sm_uiFenceNumber ++;
  483. if(!sm_uiFenceNumber)sm_uiFenceNumber = 1;
  484. pDealloc->m_pNext = m_pPendingFreeBlock;
  485. gpGcmDrawState->SetWriteBackEndLabel(GCM_LABEL_MEMORY_FREE, sm_uiFenceNumber);
  486. m_pPendingFreeBlock = pDealloc;
  487. TrackAllocStats( pBlock->MutableType(), - pBlock->MutableSize() );
  488. if ( pBlock->MutableIndex() != ~0 )
  489. {
  490. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  491. if ( m_arrAllocations[ pBlock->MutableIndex() ] != pBlock )
  492. Error( "<vitaliy> GCM Local Memory Allocator Error (freeing block that is not properly registered)!" );
  493. #endif
  494. m_arrAllocations[ pBlock->MutableIndex() ] = &pDealloc->m_block;
  495. }
  496. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  497. pBlock->MutableOffset() = ~0;
  498. pBlock->MutableIndex() = ~0;
  499. #endif
  500. }
  501. inline bool CPs3gcmLocalMemoryAllocator::IsFenceCompleted( uint32 uiCurrentFenceValue, uint32 uiCheckStoredFenceValue )
  502. {
  503. #if GCM_ALLOW_NULL_FLIPS
  504. extern bool g_ps3_nullflips;
  505. if ( g_ps3_nullflips )
  506. return true;
  507. #endif
  508. // Needs to handle the counter wrapping around
  509. return ( ( uiCurrentFenceValue - m_uiFenceLastKnown ) >= ( uiCheckStoredFenceValue - m_uiFenceLastKnown ) );
  510. }
  511. inline uint32 CPs3gcmLocalMemoryAllocator::Reclaim( bool bForce )
  512. {
  513. uint32 uiLargestBlockSizeReclaimed = 0;
  514. uint32 uiCurrentFenceValue = *sm_puiFenceLocation;
  515. // Walk pending free blocks and see if they are no longer
  516. // in use by RSX:
  517. LocalMemoryAllocation_t **p = &m_pPendingFreeBlock;
  518. if ( !bForce ) while ( (*p) && !IsFenceCompleted( uiCurrentFenceValue, (*p)->m_uiFenceNumber ) )
  519. p = &( (*p)->m_pNext );
  520. // Now p is pointing to the chain of free blocks
  521. // chain that has been completed (due to the nature of
  522. // pushing new deallocation at the head of the pending
  523. // list)
  524. if ( *p )
  525. {
  526. LocalMemoryAllocation_t *pCompletedChain = *p;
  527. *p = NULL; // Terminate the chain
  528. // Handle the special case of malloc reclaim - free all memory
  529. if ( this == &g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolMallocMemory ] )
  530. {
  531. MEM_ALLOC_CREDIT_( "GCM Malloc Pool" );
  532. for ( LocalMemoryAllocation_t *pActualFree = pCompletedChain; pActualFree; )
  533. {
  534. MemAlloc_FreeAligned( pActualFree->m_block.DataInMallocMemory() );
  535. LocalMemoryAllocation_t *pDelete = pActualFree;
  536. pActualFree = pActualFree->m_pNext;
  537. delete pDelete;
  538. }
  539. pCompletedChain = NULL;
  540. }
  541. // Relink the completed pending chain into
  542. // the free blocks chain
  543. LocalMemoryAllocation_t **ppFree = &m_pFreeBlock;
  544. while ( *ppFree )
  545. ppFree = &( (*ppFree)->m_pNext );
  546. *ppFree = pCompletedChain;
  547. // Recompute actual free sizes of the completed chain
  548. // Actual free size is the delta between block offset and next block offset
  549. // When there's no next block then its delta between block offset and unallocated edge
  550. for ( LocalMemoryAllocation_t *pActualFree = pCompletedChain; pActualFree; pActualFree = pActualFree->m_pNext )
  551. {
  552. uint32 uiIdx = pActualFree->m_block.MutableIndex() + 1;
  553. uint32 uiNextOffset = m_nOffsetUnallocated;
  554. if ( uiIdx < m_arrAllocations.Count() )
  555. {
  556. CPs3gcmLocalMemoryBlockMutable * RESTRICT pNextBlock = m_arrAllocations[ uiIdx ];
  557. uiNextOffset = pNextBlock->Offset();
  558. }
  559. uint32 uiActualBlockSize = uiNextOffset - pActualFree->m_block.Offset();
  560. pActualFree->m_block.MutableSize() = uiActualBlockSize;
  561. uiLargestBlockSizeReclaimed = MAX( uiLargestBlockSizeReclaimed, uiActualBlockSize );
  562. }
  563. }
  564. // Remember the last known fence value
  565. m_uiFenceLastKnown = uiCurrentFenceValue;
  566. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  567. ValidateAllBlocks();
  568. #endif
  569. return uiLargestBlockSizeReclaimed;
  570. }
  571. inline CPs3gcmLocalMemoryAllocator::LocalMemoryAllocation_t * CPs3gcmLocalMemoryAllocator::FindFreeBlock( uint32 uiAlignBytes, uint32 uiSize )
  572. {
  573. LocalMemoryAllocation_t **ppBest = NULL;
  574. uint32 uiSizeMax = uiSize * 11/10; // we don't want to inflate requested size by > 10%
  575. for ( LocalMemoryAllocation_t **p = &m_pFreeBlock;
  576. (*p);
  577. p = &( (*p)->m_pNext ) )
  578. {
  579. if ( (*p)->m_block.MutableSize() >= uiSize && (*p)->m_block.MutableSize() <= uiSizeMax &&
  580. !( (*p)->m_block.Offset() & ( uiAlignBytes - 1 ) ) )
  581. {
  582. if ( !ppBest || ( (*p)->m_block.MutableSize() <= (*ppBest)->m_block.MutableSize() ) )
  583. {
  584. ppBest = p;
  585. }
  586. }
  587. }
  588. if ( ppBest )
  589. {
  590. LocalMemoryAllocation_t *pFree = (*ppBest);
  591. (*ppBest) = pFree->m_pNext;
  592. pFree->m_pNext = NULL;
  593. return pFree;
  594. }
  595. return NULL;
  596. }
  597. inline bool TrackAllocStats_Pool( CPs3gcmAllocationType_t uAllocType, int nDelta )
  598. {
  599. CPs3gcmAllocationPool_t pool = PS3GCMALLOCATIONPOOL( uAllocType );
  600. int *stat = &g_RsxMemoryStats_Pool.nUnknownPoolUsed;
  601. bool bInRSXMem = true;
  602. switch( pool )
  603. {
  604. case kGcmAllocPoolDefault:
  605. stat = &g_RsxMemoryStats_Pool.nDefaultPoolUsed;
  606. break;
  607. case kGcmAllocPoolDynamicNewPath:
  608. case kGcmAllocPoolDynamic:
  609. stat = &g_RsxMemoryStats_Pool.nDynamicPoolUsed;
  610. break;
  611. case kGcmAllocPoolTiledColorFB:
  612. case kGcmAllocPoolTiledColorFBQ:
  613. case kGcmAllocPoolTiledColor512:
  614. case kGcmAllocPoolTiledColorMisc:
  615. case kGcmAllocPoolTiledD24S8:
  616. stat = &g_RsxMemoryStats_Pool.nRTPoolUsed;
  617. break;
  618. case kGcmAllocPoolMainMemory: // Unused, unless PS3GCM_VBIB_IN_IO_MEMORY set to 1
  619. case kGcmAllocPoolMallocMemory:
  620. stat = &g_RsxMemoryStats_Pool.nMainMemUsed;
  621. bInRSXMem = false; // In main memory!
  622. break;
  623. }
  624. *stat += nDelta;
  625. Assert( 0 <= (int)*stat );
  626. // Report free memory only from the default pool (the other pools are pre-sized to fixed limits, and all
  627. // geom/textures go into the default pool, so that's where content-driven variation/failures will occur)
  628. g_RsxMemoryStats.nGPUMemFree = g_RsxMemoryStats_Pool.nDefaultPoolSize - g_RsxMemoryStats_Pool.nDefaultPoolUsed;
  629. return bInRSXMem;
  630. }
  631. inline void CPs3gcmLocalMemoryAllocator::TrackAllocStats( CPs3gcmAllocationType_t uAllocType, int nDelta )
  632. {
  633. #if TRACK_ALLOC_STATS
  634. // Early-out for allocations not in RSX memory:
  635. if ( !TrackAllocStats_Pool( uAllocType, nDelta ) )
  636. return;
  637. unsigned int *stat = &g_RsxMemoryStats.nUnknown;
  638. switch( uAllocType )
  639. {
  640. case kAllocPs3gcmColorBufferMisc:
  641. case kAllocPs3gcmColorBufferFB:
  642. case kAllocPs3gcmColorBufferFBQ:
  643. case kAllocPs3gcmColorBuffer512:
  644. case kAllocPs3gcmDepthBuffer:
  645. stat = &g_RsxMemoryStats.nRTSize;
  646. break;
  647. case kAllocPs3gcmTextureData:
  648. case kAllocPs3gcmTextureData0:
  649. stat = &g_RsxMemoryStats.nTextureSize;
  650. break;
  651. case kAllocPs3GcmVertexBuffer:
  652. stat = &g_RsxMemoryStats.nVBSize;
  653. break;
  654. case kAllocPs3GcmIndexBuffer:
  655. stat = &g_RsxMemoryStats.nIBSize;
  656. break;
  657. case kAllocPs3GcmShader:
  658. case kAllocPs3GcmEdgeGeomBuffer:
  659. case kAllocPs3GcmVertexBufferDynamic:
  660. case kAllocPs3GcmIndexBufferDynamic:
  661. case kAllocPs3GcmDynamicBufferPool:
  662. case kAllocPs3GcmVertexBufferDma:
  663. case kAllocPs3GcmIndexBufferDma:
  664. // Treat these as misc unless they become big/variable
  665. break;
  666. }
  667. *stat += nDelta;
  668. Assert( 0 <= (int)*stat );
  669. #endif // TRACK_ALLOC_STATS
  670. }
  671. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  672. #define VALIDATECONDITION( x ) if( !( x ) ) { Error( "<vitaliy> GCM Local Memory Allocation block %p index %d is corrupt [line %d]!\n", pBlock, k, __LINE__ ); }
  673. inline void CPs3gcmLocalMemoryAllocator::ValidateAllBlocks()
  674. {
  675. // Traverse the allocated list and validate debug guards and patch-back indices
  676. CUtlVector< uint32 > arrFreeBlocksIdx;
  677. uint32 uiLastAllocatedOffset = m_nOffsetMin;
  678. for ( int k = 0, kEnd = m_arrAllocations.Count(); k < kEnd; ++ k )
  679. {
  680. CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock = m_arrAllocations[k];
  681. VALIDATECONDITION( pBlock );
  682. VALIDATECONDITION( pBlock->m_dbgGuardCookie == g_GcmLocalMemoryBlockDebugCookieAllocated || pBlock->m_dbgGuardCookie == g_GcmLocalMemoryBlockDebugCookieFree );
  683. VALIDATECONDITION( pBlock->MutableIndex() < m_arrAllocations.Count() );
  684. VALIDATECONDITION( pBlock->MutableIndex() == k );
  685. VALIDATECONDITION( m_arrAllocations[ pBlock->MutableIndex() ] == pBlock );
  686. VALIDATECONDITION( pBlock->Offset() >= uiLastAllocatedOffset );
  687. uiLastAllocatedOffset = pBlock->Offset() + pBlock->MutableSize();
  688. VALIDATECONDITION( uiLastAllocatedOffset <= m_nOffsetMax );
  689. if ( pBlock->m_dbgGuardCookie == g_GcmLocalMemoryBlockDebugCookieFree )
  690. arrFreeBlocksIdx.AddToTail( k );
  691. }
  692. // Traverse free lists and validate
  693. LocalMemoryAllocation_t * arrFree[] = { m_pPendingFreeBlock, m_pFreeBlock };
  694. for ( int j = 0; j < ARRAYSIZE( arrFree ); ++ j )
  695. for ( LocalMemoryAllocation_t *p = arrFree[j]; p; p = p->m_pNext )
  696. {
  697. int k = j;
  698. CPs3gcmLocalMemoryBlockMutable * RESTRICT pBlock = &p->m_block;
  699. VALIDATECONDITION( pBlock );
  700. VALIDATECONDITION( pBlock->m_dbgGuardCookie == g_GcmLocalMemoryBlockDebugCookieFree );
  701. k = pBlock->MutableIndex();
  702. if ( pBlock->MutableIndex() != ~0 )
  703. {
  704. VALIDATECONDITION( pBlock->MutableIndex() < m_arrAllocations.Count() );
  705. VALIDATECONDITION( m_arrAllocations[ pBlock->MutableIndex() ] == pBlock );
  706. VALIDATECONDITION( arrFreeBlocksIdx.FindAndFastRemove( pBlock->MutableIndex() ) );
  707. }
  708. }
  709. int k = 0;
  710. void *pBlock = 0;
  711. VALIDATECONDITION( !arrFreeBlocksIdx.Count() );
  712. }
  713. #endif
  714. inline void CPs3gcmLocalMemoryAllocator::Compact()
  715. {
  716. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  717. ValidateAllBlocks();
  718. if ( r_ps3_gcmnocompact.GetBool() )
  719. return;
  720. #endif
  721. // Reclaim all memory (NOTE: all pending blocks must be reclaimed since both RSX and PPU have stopped rendering!)
  722. Reclaim();
  723. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  724. if ( m_pPendingFreeBlock )
  725. Warning( "GCM Local Memory Allocator Compact forces pending free blocks to be reclaimed.\n" );
  726. ValidateAllBlocks();
  727. #endif
  728. if ( m_pPendingFreeBlock )
  729. Reclaim( true );
  730. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  731. if ( m_pPendingFreeBlock )
  732. Error( "<vitaliy> GCM Local Memory Allocator Compact requires RSX and PPU rendering to be paused! (pending free blocks have not been reclaimed)\n" );
  733. ValidateAllBlocks();
  734. #endif
  735. // Walk the free blocks chain and patch-back NULL pointers into allocation tracking system
  736. while ( m_pFreeBlock )
  737. {
  738. LocalMemoryAllocation_t *p = m_pFreeBlock;
  739. m_pFreeBlock = p->m_pNext;
  740. m_arrAllocations[ p->m_block.MutableIndex() ] = NULL;
  741. delete p;
  742. }
  743. Assert( !m_pFreeBlock && !m_pPendingFreeBlock );
  744. // These are elements requiring reallocation
  745. uint32 uiCount = m_arrAllocations.Count();
  746. CPs3gcmLocalMemoryBlockMutable **pReallocationBlocks = m_arrAllocations.Base();
  747. // Here "correct" implementation would be to copy off m_arrAllocations vector onto stack for iteration,
  748. // RemoveAll from m_arrAllocations vector and allocate all blocks again.
  749. // We will cheat since we know that we will allocate same number of elements and directly write zero
  750. // into m_arrAllocations m_Size member, then we will still be able to use the memory of the vector
  751. // for reading blocks requiring compact reallocation, and AddToTail will still fill the vector with
  752. // correct data.
  753. struct AllocatorCompactVectorCheat : public CUtlVector< CPs3gcmLocalMemoryBlockMutable * > { inline void ResetCountPreservingMemoryContents() { m_Size = 0; } };
  754. ( ( AllocatorCompactVectorCheat * ) ( char * ) &m_arrAllocations )->ResetCountPreservingMemoryContents();
  755. m_nOffsetUnallocated = m_nOffsetMin;
  756. // Prepare RSX for data buffer transfers in local memory
  757. uint nTransferMode = ( ( this - &g_ps3gcmLocalMemoryAllocator[ kGcmAllocPoolDefault ] ) < kGcmAllocPoolMainMemory ) ? CELL_GCM_TRANSFER_LOCAL_TO_LOCAL : CELL_GCM_TRANSFER_MAIN_TO_MAIN;
  758. Assert( nTransferMode < 4 );
  759. // Reallocate all blocks
  760. for ( ; uiCount; -- uiCount, ++ pReallocationBlocks )
  761. {
  762. CPs3gcmLocalMemoryBlockMutable *pBlock = *pReallocationBlocks;
  763. if ( !pBlock )
  764. continue;
  765. uint32 nOldOffset = pBlock->Offset();
  766. char* pOldAddress = pBlock->DataInAnyMemory();
  767. TrackAllocStats( pBlock->MutableType(), - pBlock->MutableSize() );
  768. Alloc( pBlock );
  769. if ( nOldOffset == pBlock->Offset() )
  770. continue;
  771. // Have RSX transfer blocks data. RSX may hang if there's WriteLabel between the Format and Offset commands,
  772. // so reserve space for both of them up front
  773. // SpuDrawTransfer_t * pTransfer = g_spuGcm.GetDrawQueue()->AllocWithHeader<SpuDrawTransfer_t>( SPUDRAWQUEUE_TRANSFER_METHOD | nTransferMode );
  774. // pTransfer->m_nLineSize = pBlock->MutableSize();
  775. // pTransfer->m_nOldOffset = nOldOffset;
  776. // pTransfer->m_nNewOffset = pBlock->Offset();
  777. // 7LTODO
  778. uint32 uiLineSize = pBlock->MutableSize();
  779. uint32 uiLineOffset = 0;
  780. const uint nMaxTransferSize = 0x3FFFFF;
  781. cellGcmReserveMethodSizeInline(gpGcmContext, 0x4000/4);
  782. GCM_FUNC( cellGcmSetTransferDataMode, nTransferMode );
  783. int i = 1;
  784. do
  785. {
  786. uint32 uiTransferSize = Min<uint32>( uiLineSize, nMaxTransferSize );
  787. GCM_FUNC( cellGcmSetTransferDataFormat, 0, 0, uiTransferSize, 1, 1, 1 );
  788. GCM_FUNC( cellGcmSetTransferDataOffset, pBlock->Offset() + uiLineOffset, nOldOffset + uiLineOffset );
  789. uiLineSize -= uiTransferSize;
  790. uiLineOffset += uiTransferSize;
  791. i++;
  792. }
  793. while ( uiLineSize > 0 );
  794. // V_memmove(pBlock->DataInAnyMemory(), pOldAddress, pBlock->MutableSize() );
  795. }
  796. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  797. ValidateAllBlocks();
  798. #endif
  799. }
  800. //////////////////////////////////////////////////////////////////////////
  801. //
  802. // Computation of tiled memory
  803. //
  804. uint32 CPs3gcmLocalMemoryBlock::TiledMemoryTagAreaBase() const
  805. {
  806. CPs3gcmAllocationPool_t ePool = PS3GCMALLOCATIONPOOL(m_uType);
  807. if ( ePool == kGcmAllocPoolTiledColorMisc ) // Misc color tiles are placed at the front of tag area after preset pools
  808. return ( Offset() - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFB].m_nOffsetMin ) / 0x10000;
  809. if ( ePool == kGcmAllocPoolTiledD24S8 ) // Depth tiles are placed in the end of tag area (0-0x7FF is offset range)
  810. return 0x800 - ( Offset() - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledD24S8].m_nOffsetMin + m_uiSize ) / 0x10000;
  811. if ( ePool == kGcmAllocPoolTiledColorFB ) // FB color tiles go first
  812. return ( g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFB].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFB].m_nOffsetMin ) / 0x10000;
  813. if ( ePool == kGcmAllocPoolTiledColorFBQ ) // FBQ color tiles go next
  814. return ( g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFBQ].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFB].m_nOffsetMin ) / 0x10000;
  815. if ( ePool == kGcmAllocPoolTiledColor512 ) // 512 color tiles go next
  816. return ( g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColor512].m_nOffsetMin - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledColorFB].m_nOffsetMin ) / 0x10000;
  817. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  818. Error( "<vitaliy> Cannot compute tiled memory tag base from a non-tiled-pool allocation!\n" );
  819. #endif
  820. return ~0;
  821. }
  822. uint32 CPs3gcmLocalMemoryBlock::TiledMemoryIndex() const
  823. {
  824. CPs3gcmAllocationPool_t ePool = PS3GCMALLOCATIONPOOL(m_uType);
  825. if ( ePool == kGcmAllocPoolTiledColorMisc ) // Color tiles are placed in the front
  826. return m_uiIndex + kGcmAllocPoolTiledColorMisc - kGcmAllocPoolTiledColorFB;
  827. if ( ePool == kGcmAllocPoolTiledD24S8 ) // Depth tiles are placed as last tiles
  828. return 14 - m_uiIndex;
  829. return ePool - kGcmAllocPoolTiledColorFB;
  830. }
  831. uint32 CPs3gcmLocalMemoryBlock::ZcullMemoryIndex() const
  832. {
  833. CPs3gcmAllocationPool_t ePool = PS3GCMALLOCATIONPOOL(m_uType);
  834. if ( ePool == kGcmAllocPoolTiledD24S8 ) // Depth tiles are the only zcull tiles
  835. return m_uiIndex;
  836. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  837. Error( "<vitaliy> Cannot compute zcull index from a non-zcull allocation!\n" );
  838. #endif
  839. return ~0;
  840. }
  841. uint32 CPs3gcmLocalMemoryBlock::ZcullMemoryStart() const
  842. {
  843. CPs3gcmAllocationPool_t ePool = PS3GCMALLOCATIONPOOL(m_uType);
  844. if ( ePool == kGcmAllocPoolTiledD24S8 ) // Depth tiles are the only zcull tiles
  845. return ( Offset() - g_ps3gcmLocalMemoryAllocator[kGcmAllocPoolTiledD24S8].m_nOffsetMin ) / 4; // 1 byte per pixel, D24S8 is 4 bytes per pixel, implicitly 4096 aligned because offset is 64Kb aligned
  846. #ifdef GCMLOCALMEMORYBLOCKDEBUG
  847. Error( "<vitaliy> Cannot compute zcull memory start from a non-zcull allocation!\n" );
  848. #endif
  849. return ~0;
  850. }
  851. //////////////////////////////////////////////////////////////////////////
  852. //
  853. // Allow shaderapi to query GPU memory stats:
  854. //
  855. void GetGPUMemoryStats( GPUMemoryStats &stats )
  856. {
  857. stats = g_RsxMemoryStats;
  858. }