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.

778 lines
35 KiB

  1. //===== Copyright 1996-2007, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: tracks VB allocations (and compressed/uncompressed vertex memory usage)
  4. //
  5. //===========================================================================//
  6. #include "materialsystem/imaterial.h"
  7. #include "imeshdx8.h"
  8. #include "convar.h"
  9. #include "tier1/utlhash.h"
  10. #include "tier1/utlstack.h"
  11. #include "materialsystem/ivballoctracker.h"
  12. // NOTE: This has to be the last file included!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. //
  16. // Types
  17. //
  18. //-----------------------------------------------------------------------------
  19. #if ENABLE_VB_ALLOC_TRACKER
  20. // FIXME: combine this into the lower bits of VertexFormat_t
  21. typedef uint64 VertexElementMap_t;
  22. enum Saving_t
  23. {
  24. SAVING_COMPRESSION = 0,
  25. SAVING_REMOVAL = 1,
  26. SAVING_ALIGNMENT = 2
  27. };
  28. struct ElementData
  29. {
  30. VertexElement_t element;
  31. int uncompressed; // uncompressed vertex size
  32. int currentCompressed; // current compressed vertex element size
  33. int idealCompressed; // ideal future compressed vertex element size
  34. const char *name;
  35. };
  36. class CounterData
  37. {
  38. public:
  39. CounterData() : m_memCount( 0 ), m_vertCount( 0 ), m_paddingCount( 0 )
  40. {
  41. for ( int i = 0; i < VERTEX_ELEMENT_NUMELEMENTS; i++ )
  42. {
  43. m_elementsCompressed[ i ] = 0;
  44. m_elementsUncompressed[ i ] = 0;
  45. }
  46. m_AllocatorName[ 0 ] = 0;
  47. }
  48. static const int MAX_NAME_SIZE = 128;
  49. int m_memCount;
  50. int m_vertCount;
  51. int m_paddingCount;
  52. int m_elementsCompressed[ VERTEX_ELEMENT_NUMELEMENTS ]; // Number of compressed verts using each element
  53. int m_elementsUncompressed[ VERTEX_ELEMENT_NUMELEMENTS ]; // Number of uncompressed verts using each element
  54. char m_AllocatorName[ MAX_NAME_SIZE ];
  55. };
  56. class AllocData
  57. {
  58. public:
  59. AllocData( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, int allocatorHash )
  60. : m_buffer( buffer ), m_bufferSize( bufferSize ), m_fmt( fmt ), m_numVerts( numVerts ), m_allocatorHash( allocatorHash ) {}
  61. AllocData() : m_buffer( NULL ), m_bufferSize( 0 ), m_fmt( 0 ), m_numVerts( 0 ), m_allocatorHash( 0 ) {}
  62. VertexFormat_t m_fmt;
  63. void * m_buffer;
  64. int m_bufferSize;
  65. int m_numVerts;
  66. short m_allocatorHash;
  67. };
  68. typedef CUtlHashFixed < CounterData, 64 > CCounterTable;
  69. typedef CUtlHashFixed < AllocData, 4096 > CAllocTable;
  70. typedef CUtlStack < short > CAllocNameHashes;
  71. #endif // ENABLE_VB_ALLOC_TRACKER
  72. class CVBAllocTracker : public IVBAllocTracker
  73. {
  74. public:
  75. virtual void CountVB( void * buffer, bool isDynamic, int bufferSize, int vertexSize, VertexFormat_t fmt );
  76. virtual void UnCountVB( void * buffer );
  77. virtual bool TrackMeshAllocations( const char * allocatorName );
  78. void DumpVBAllocs();
  79. #if ENABLE_VB_ALLOC_TRACKER
  80. public:
  81. CVBAllocTracker();
  82. private:
  83. UtlHashFixedHandle_t TrackAlloc( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, short allocatorHash );
  84. bool KillAlloc( void * buffer, int & bufferSize, VertexFormat_t & fmt, int & numVerts, short & allocatorHash );
  85. UtlHashFixedHandle_t GetCounterHandle( const char * allocatorName, short allocatorHash );
  86. void SpewElements( const char * allocatorName, short nameHash );
  87. int ComputeVertexSize( VertexElementMap_t map, VertexFormat_t fmt, bool compressed );
  88. VertexElementMap_t ComputeElementMap( VertexFormat_t fmt, int vertexSize, bool isDynamic );
  89. void UpdateElements( CounterData & data, VertexFormat_t fmt, int numVerts, int vertexSize,
  90. bool isDynamic, bool isCompressed );
  91. int ComputeAlignmentWastage( int bufferSize );
  92. void AddSaving( int & alreadySaved, int & yetToSave, const char *allocatorName, VertexElement_t element, Saving_t savingType );
  93. void SpewExpectedSavings( void );
  94. void UpdateData( const char * allocatorName, short allocatorKey, int bufferSize, VertexFormat_t fmt,
  95. int numVerts, int vertexSize, bool isDynamic, bool isCompressed );
  96. const char * GetNameString( int allocatorKey );
  97. void SpewData( const char * allocatorName, short nameHash = 0 );
  98. void SpewDataSometimes( int inc );
  99. static const int SPEW_RATE = 64;
  100. static const int MAX_ALLOCATOR_NAME_SIZE = 128;
  101. char m_MeshAllocatorName[ MAX_ALLOCATOR_NAME_SIZE ];
  102. bool m_bSuperSpew;
  103. CCounterTable m_VBCountTable;
  104. CAllocTable m_VBAllocTable;
  105. CAllocNameHashes m_VBTableNameHashes;
  106. // We use a mutex since allocation tracking is accessed from multiple loading threads.
  107. // CThreadFastMutex is used as contention is expected to be low during loading.
  108. CThreadFastMutex m_VBAllocMutex;
  109. #endif // ENABLE_VB_ALLOC_TRACKER
  110. };
  111. //-----------------------------------------------------------------------------
  112. //
  113. // Global data
  114. //
  115. //-----------------------------------------------------------------------------
  116. #if ENABLE_VB_ALLOC_TRACKER
  117. // FIXME: do this in a better way:
  118. static const ElementData positionElement = { VERTEX_ELEMENT_POSITION, 12, 12, 8, "POSITION " }; // (UNDONE: need vertex shader to scale, may cause cracking w/ static props)
  119. static const ElementData position4DElement = { VERTEX_ELEMENT_POSITION4D, 16, 16, 8, "POSITION4D " }; // Not really a position as used by SubD path
  120. static const ElementData normalElement = { VERTEX_ELEMENT_NORMAL, 12, 4, 4, "NORMAL " }; // (UNDONE: PC (2x16-byte Raphael method) or 360 (D3DDECLTYPE_HEND3N))
  121. static const ElementData normal4DElement = { VERTEX_ELEMENT_NORMAL4D, 16, 16, 8, "NORMAL4D " }; // Not really a normal as used by SubD path
  122. static const ElementData colorElement = { VERTEX_ELEMENT_COLOR, 4, 4, 4, "COLOR " }; // (already minimal)
  123. static const ElementData specularElement = { VERTEX_ELEMENT_SPECULAR, 4, 4, 4, "SPECULAR " }; // (already minimal)
  124. static const ElementData tangentSElement = { VERTEX_ELEMENT_TANGENT_S, 12, 12, 4, "TANGENT_S " }; // (all-but-unused)
  125. static const ElementData tangentTElement = { VERTEX_ELEMENT_TANGENT_T, 12, 12, 4, "TANGENT_T " }; // (all-but-unused)
  126. static const ElementData wrinkleElement = { VERTEX_ELEMENT_WRINKLE, 4, 4, 0, "WRINKLE " }; // (UNDONE: compress it as a SHORTN in Position.w - is it [0,1]?)
  127. static const ElementData boneIndexElement = { VERTEX_ELEMENT_BONEINDEX, 4, 4, 4, "BONEINDEX " }; // (already minimal)
  128. static const ElementData boneWeight1Element = { VERTEX_ELEMENT_BONEWEIGHTS1, 4, 4, 4, "BONEWEIGHT1 " }; // (unused)
  129. static const ElementData boneWeight2Element = { VERTEX_ELEMENT_BONEWEIGHTS2, 8, 8, 4, "BONEWEIGHT2 " }; // (UNDONE: take care w.r.t cracking in flex regions)
  130. static const ElementData boneWeight3Element = { VERTEX_ELEMENT_BONEWEIGHTS3, 12, 12, 8, "BONEWEIGHT3 " }; // (unused)
  131. static const ElementData boneWeight4Element = { VERTEX_ELEMENT_BONEWEIGHTS4, 16, 16, 8, "BONEWEIGHT4 " }; // (unused)
  132. static const ElementData userData1Element = { VERTEX_ELEMENT_USERDATA1, 4, 4, 4, "USERDATA1 " }; // (unused)
  133. static const ElementData userData2Element = { VERTEX_ELEMENT_USERDATA2, 8, 8, 4, "USERDATA2 " }; // (unused)
  134. static const ElementData userData3Element = { VERTEX_ELEMENT_USERDATA3, 12, 12, 4, "USERDATA3 " }; // (unused)
  135. #if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 )
  136. static const ElementData userData4Element = { VERTEX_ELEMENT_USERDATA4, 16, 4, 4, "USERDATA4 " }; // (UNDONE: PC (2x16-byte Raphael method) or 360 (D3DDECLTYPE_HEND3N))
  137. #else // ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 )
  138. static const ElementData userData4Element = { VERTEX_ELEMENT_USERDATA4, 16, 0, 0, "USERDATA4 " }; // (UNDONE: PC (2x16-byte Raphael method) or 360 (D3DDECLTYPE_HEND3N))
  139. #endif
  140. static const ElementData texCoord1D0Element = { VERTEX_ELEMENT_TEXCOORD1D_0, 4, 4, 4, "TEXCOORD1D_0" }; // (not worth compressing)
  141. static const ElementData texCoord1D1Element = { VERTEX_ELEMENT_TEXCOORD1D_1, 4, 4, 4, "TEXCOORD1D_1" }; // (not worth compressing)
  142. static const ElementData texCoord1D2Element = { VERTEX_ELEMENT_TEXCOORD1D_2, 4, 4, 4, "TEXCOORD1D_2" }; // (not worth compressing)
  143. static const ElementData texCoord1D3Element = { VERTEX_ELEMENT_TEXCOORD1D_3, 4, 4, 4, "TEXCOORD1D_3" }; // (not worth compressing)
  144. static const ElementData texCoord1D4Element = { VERTEX_ELEMENT_TEXCOORD1D_4, 4, 4, 4, "TEXCOORD1D_4" }; // (not worth compressing)
  145. static const ElementData texCoord1D5Element = { VERTEX_ELEMENT_TEXCOORD1D_5, 4, 4, 4, "TEXCOORD1D_5" }; // (not worth compressing)
  146. static const ElementData texCoord1D6Element = { VERTEX_ELEMENT_TEXCOORD1D_6, 4, 4, 4, "TEXCOORD1D_6" }; // (not worth compressing)
  147. static const ElementData texCoord1D7Element = { VERTEX_ELEMENT_TEXCOORD1D_7, 4, 4, 4, "TEXCOORD1D_7" }; // (not worth compressing)
  148. static const ElementData texCoord2D0Element = { VERTEX_ELEMENT_TEXCOORD2D_0, 8, 8, 4, "TEXCOORD2D_0" }; // (UNDONE: need vertex shader to take scale, account for clamping)
  149. static const ElementData texCoord2D1Element = { VERTEX_ELEMENT_TEXCOORD2D_1, 8, 8, 4, "TEXCOORD2D_1" }; // (UNDONE: need vertex shader to take scale, account for clamping)
  150. static const ElementData texCoord2D2Element = { VERTEX_ELEMENT_TEXCOORD2D_2, 8, 8, 4, "TEXCOORD2D_2" }; // (all-but-unused)
  151. static const ElementData texCoord2D3Element = { VERTEX_ELEMENT_TEXCOORD2D_3, 8, 8, 4, "TEXCOORD2D_3" }; // (unused)
  152. static const ElementData texCoord2D4Element = { VERTEX_ELEMENT_TEXCOORD2D_4, 8, 8, 4, "TEXCOORD2D_4" }; // (unused)
  153. static const ElementData texCoord2D5Element = { VERTEX_ELEMENT_TEXCOORD2D_5, 8, 8, 4, "TEXCOORD2D_5" }; // (unused)
  154. static const ElementData texCoord2D6Element = { VERTEX_ELEMENT_TEXCOORD2D_6, 8, 8, 4, "TEXCOORD2D_6" }; // (unused)
  155. static const ElementData texCoord2D7Element = { VERTEX_ELEMENT_TEXCOORD2D_7, 8, 8, 4, "TEXCOORD2D_7" }; // (unused)
  156. static const ElementData texCoord3D0Element = { VERTEX_ELEMENT_TEXCOORD3D_0, 12, 12, 8, "TEXCOORD3D_0" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  157. static const ElementData texCoord3D1Element = { VERTEX_ELEMENT_TEXCOORD3D_1, 12, 12, 8, "TEXCOORD3D_1" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  158. static const ElementData texCoord3D2Element = { VERTEX_ELEMENT_TEXCOORD3D_2, 12, 12, 8, "TEXCOORD3D_2" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  159. static const ElementData texCoord3D3Element = { VERTEX_ELEMENT_TEXCOORD3D_3, 12, 12, 8, "TEXCOORD3D_3" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  160. static const ElementData texCoord3D4Element = { VERTEX_ELEMENT_TEXCOORD3D_4, 12, 12, 8, "TEXCOORD3D_4" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  161. static const ElementData texCoord3D5Element = { VERTEX_ELEMENT_TEXCOORD3D_5, 12, 12, 8, "TEXCOORD3D_5" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  162. static const ElementData texCoord3D6Element = { VERTEX_ELEMENT_TEXCOORD3D_6, 12, 12, 8, "TEXCOORD3D_6" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  163. static const ElementData texCoord3D7Element = { VERTEX_ELEMENT_TEXCOORD3D_7, 12, 12, 8, "TEXCOORD3D_7" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  164. static const ElementData texCoord4D0Element = { VERTEX_ELEMENT_TEXCOORD4D_0, 16, 16, 8, "TEXCOORD4D_0" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  165. static const ElementData texCoord4D1Element = { VERTEX_ELEMENT_TEXCOORD4D_1, 16, 16, 8, "TEXCOORD4D_1" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  166. static const ElementData texCoord4D2Element = { VERTEX_ELEMENT_TEXCOORD4D_2, 16, 16, 8, "TEXCOORD4D_2" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  167. static const ElementData texCoord4D3Element = { VERTEX_ELEMENT_TEXCOORD4D_3, 16, 16, 8, "TEXCOORD4D_3" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  168. static const ElementData texCoord4D4Element = { VERTEX_ELEMENT_TEXCOORD4D_4, 16, 16, 8, "TEXCOORD4D_4" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  169. static const ElementData texCoord4D5Element = { VERTEX_ELEMENT_TEXCOORD4D_5, 16, 16, 8, "TEXCOORD4D_5" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  170. static const ElementData texCoord4D6Element = { VERTEX_ELEMENT_TEXCOORD4D_6, 16, 16, 8, "TEXCOORD4D_6" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  171. static const ElementData texCoord4D7Element = { VERTEX_ELEMENT_TEXCOORD4D_7, 16, 16, 8, "TEXCOORD4D_7" }; // FIXME: used how much? (UNDONE: need vertex shader to take scale, account for clamping)
  172. static const ElementData elementTable[] = { positionElement,
  173. position4DElement,
  174. normalElement,
  175. normal4DElement,
  176. colorElement,
  177. specularElement,
  178. tangentSElement,
  179. tangentTElement,
  180. wrinkleElement,
  181. boneIndexElement,
  182. boneWeight1Element, boneWeight2Element, boneWeight3Element, boneWeight4Element,
  183. userData1Element, userData2Element, userData3Element, userData4Element,
  184. texCoord1D0Element, texCoord1D1Element, texCoord1D2Element, texCoord1D3Element, texCoord1D4Element, texCoord1D5Element, texCoord1D6Element, texCoord1D7Element,
  185. texCoord2D0Element, texCoord2D1Element, texCoord2D2Element, texCoord2D3Element, texCoord2D4Element, texCoord2D5Element, texCoord2D6Element, texCoord2D7Element,
  186. texCoord3D0Element, texCoord3D1Element, texCoord3D2Element, texCoord3D3Element, texCoord3D4Element, texCoord3D5Element, texCoord3D6Element, texCoord3D7Element,
  187. texCoord4D0Element, texCoord4D1Element, texCoord4D2Element, texCoord4D3Element, texCoord4D4Element, texCoord4D5Element, texCoord4D6Element, texCoord4D7Element,
  188. };
  189. static ConVar mem_vballocspew( "mem_vballocspew", "0", FCVAR_CHEAT, "How often to spew vertex buffer allocation stats - 1: every alloc, 2+: every 2+ allocs, 0: off" );
  190. #endif // ENABLE_VB_ALLOC_TRACKER
  191. //-----------------------------------------------------------------------------
  192. // Singleton instance exposed to the engine
  193. //-----------------------------------------------------------------------------
  194. static CVBAllocTracker s_VBAllocTracker;
  195. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVBAllocTracker, IVBAllocTracker,
  196. VB_ALLOC_TRACKER_INTERFACE_VERSION, s_VBAllocTracker );
  197. //-----------------------------------------------------------------------------
  198. //
  199. // VB alloc-tracking code starts here
  200. //
  201. //-----------------------------------------------------------------------------
  202. #if ENABLE_VB_ALLOC_TRACKER
  203. CVBAllocTracker::CVBAllocTracker()
  204. {
  205. COMPILE_TIME_ASSERT( VERTEX_ELEMENT_NUMELEMENTS == ARRAYSIZE( elementTable ) );
  206. m_bSuperSpew = false;
  207. m_MeshAllocatorName[0] = 0;
  208. }
  209. UtlHashFixedHandle_t CVBAllocTracker::TrackAlloc( void * buffer, int bufferSize, VertexFormat_t fmt, int numVerts, short allocatorHash )
  210. {
  211. AllocData newData( buffer, bufferSize, fmt, numVerts, allocatorHash );
  212. UtlHashFixedHandle_t handle = m_VBAllocTable.Insert( (intp)buffer, newData );
  213. if ( handle == m_VBAllocTable.InvalidHandle() )
  214. {
  215. Warning( "[VBMEM] VBMemAllocTable hash collision (grow table).\n" );
  216. }
  217. return handle;
  218. }
  219. bool CVBAllocTracker::KillAlloc( void * buffer, int & bufferSize, VertexFormat_t & fmt, int & numVerts, short & allocatorHash )
  220. {
  221. UtlHashFixedHandle_t handle = m_VBAllocTable.Find( (intp)buffer );
  222. if ( handle != m_VBAllocTable.InvalidHandle() )
  223. {
  224. AllocData & data = m_VBAllocTable.Element( handle );
  225. bufferSize = data.m_bufferSize;
  226. fmt = data.m_fmt;
  227. numVerts = data.m_numVerts;
  228. allocatorHash = data.m_allocatorHash;
  229. m_VBAllocTable.Remove( handle );
  230. return true;
  231. }
  232. Warning( "[VBMEM] VBMemAllocTable failed to find alloc entry...\n" );
  233. return false;
  234. }
  235. UtlHashFixedHandle_t CVBAllocTracker::GetCounterHandle( const char * allocatorName, short allocatorHash )
  236. {
  237. UtlHashFixedHandle_t handle = m_VBCountTable.Find( allocatorHash );
  238. if ( handle == m_VBCountTable.InvalidHandle() )
  239. {
  240. CounterData newData;
  241. Assert( ( allocatorName != NULL ) && ( allocatorName[0] != 0 ) );
  242. V_strncpy( newData.m_AllocatorName, allocatorName, CounterData::MAX_NAME_SIZE );
  243. handle = m_VBCountTable.Insert( allocatorHash, newData );
  244. m_VBTableNameHashes.Push( allocatorHash );
  245. }
  246. if ( handle == m_VBCountTable.InvalidHandle() )
  247. {
  248. Warning( "[VBMEM] CounterData hash collision (grow table).\n" );
  249. }
  250. return handle;
  251. }
  252. void CheckForElementTableUpdates( const ElementData & element )
  253. {
  254. // Ensure that 'elementTable' gets updated if VertexElement_t ever changes:
  255. int tableIndex = &element - &( elementTable[0] );
  256. Assert( tableIndex == element.element );
  257. if ( tableIndex != element.element )
  258. {
  259. static int timesToSpew = 20;
  260. if ( timesToSpew > 0 )
  261. {
  262. Warning( "VertexElement_t structure has changed, ElementData table in cvballoctracker needs updating!\n" );
  263. timesToSpew--;
  264. }
  265. }
  266. }
  267. void CVBAllocTracker::SpewElements( const char * allocatorName, short nameHash )
  268. {
  269. short allocatorHash = allocatorName ? HashString( allocatorName ) : nameHash;
  270. UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorHash );
  271. if ( handle != m_VBCountTable.InvalidHandle() )
  272. {
  273. CounterData & data = m_VBCountTable.Element( handle );
  274. int originalSum = 0, currentSum = 0, idealSum = 0;
  275. for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++)
  276. {
  277. CheckForElementTableUpdates( elementTable[ i ] );
  278. int numCompressed = data.m_elementsCompressed[ i ];
  279. int numUncompressed = data.m_elementsUncompressed[ i ];
  280. int numVerts = numCompressed + numUncompressed;
  281. originalSum += numVerts*elementTable[ i ].uncompressed;
  282. currentSum += numCompressed*elementTable[ i ].currentCompressed + numUncompressed*elementTable[ i ].uncompressed;
  283. idealSum += numVerts*elementTable[ i ].idealCompressed;
  284. }
  285. if ( originalSum > 0 )
  286. {
  287. Msg( "[VBMEM] ----elements (%s)----:\n", data.m_AllocatorName);
  288. for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++)
  289. {
  290. // We count vertices (converted to bytes via elementTable)
  291. int numCompressed = data.m_elementsCompressed[ i ];
  292. int numUncompressed = data.m_elementsUncompressed[ i ];
  293. int numVerts = numCompressed + numUncompressed;
  294. const ElementData & elementData = elementTable[ i ];
  295. if ( numVerts > 0 )
  296. {
  297. Msg( " element: %5.2f MB 'U', %5.2f MB 'C', %5.2f MB 'I', %6.2f MB 'D', %s\n",
  298. numVerts*elementData.uncompressed / ( 1024.0f*1024.0f ),
  299. ( numCompressed*elementData.currentCompressed + numUncompressed*elementData.uncompressed ) / ( 1024.0f*1024.0f ),
  300. numVerts*elementData.idealCompressed / ( 1024.0f*1024.0f ),
  301. -( numCompressed*elementData.currentCompressed + numUncompressed*elementData.uncompressed - numVerts*elementData.idealCompressed ) / ( 1024.0f*1024.0f ),
  302. elementData.name );
  303. }
  304. }
  305. Msg( "[VBMEM] total: %5.2f MB 'U', %5.2f MB 'C', %5.2f MB 'I', %6.2f MB 'D'\n",
  306. originalSum / ( 1024.0f*1024.0f ),
  307. currentSum / ( 1024.0f*1024.0f ),
  308. idealSum / ( 1024.0f*1024.0f ),
  309. -( currentSum - idealSum ) / ( 1024.0f*1024.0f ) );
  310. Msg( "[VBMEM] ----elements (%s)----:\n", data.m_AllocatorName);
  311. }
  312. }
  313. }
  314. int CVBAllocTracker::ComputeVertexSize( VertexElementMap_t map, VertexFormat_t fmt, bool compressed )
  315. {
  316. int vertexSize = 0;
  317. for ( int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++ )
  318. {
  319. const ElementData & element = elementTable[ i ];
  320. CheckForElementTableUpdates( element );
  321. VertexElementMap_t LSB = 1;
  322. if ( map & ( LSB << i ) )
  323. {
  324. vertexSize += compressed ? element.currentCompressed : element.uncompressed;
  325. }
  326. }
  327. // On PC (see CVertexBufferBase::ComputeVertexDescription() in meshbase.cpp)
  328. // vertex strides are aligned to 16 bytes:
  329. bool bCacheAlign = ( fmt & VERTEX_FORMAT_USE_EXACT_FORMAT ) == 0;
  330. if ( bCacheAlign && ( vertexSize > 16 ) && IsPC() )
  331. {
  332. vertexSize = (vertexSize + 0xF) & (~0xF);
  333. }
  334. return vertexSize;
  335. }
  336. VertexElementMap_t CVBAllocTracker::ComputeElementMap( VertexFormat_t fmt, int vertexSize, bool isDynamic )
  337. {
  338. VertexElementMap_t map = 0, LSB = 1;
  339. if ( fmt & VERTEX_POSITION ) map |= LSB << VERTEX_ELEMENT_POSITION;
  340. if ( fmt & VERTEX_NORMAL ) map |= LSB << VERTEX_ELEMENT_NORMAL;
  341. if ( fmt & VERTEX_COLOR ) map |= LSB << VERTEX_ELEMENT_COLOR;
  342. if ( fmt & VERTEX_SPECULAR ) map |= LSB << VERTEX_ELEMENT_SPECULAR;
  343. if ( fmt & VERTEX_TANGENT_S ) map |= LSB << VERTEX_ELEMENT_TANGENT_S;
  344. if ( fmt & VERTEX_TANGENT_T ) map |= LSB << VERTEX_ELEMENT_TANGENT_T;
  345. if ( fmt & VERTEX_WRINKLE ) map |= LSB << VERTEX_ELEMENT_WRINKLE;
  346. if ( fmt & VERTEX_BONE_INDEX) map |= LSB << VERTEX_ELEMENT_BONEINDEX;
  347. int numBones = NumBoneWeights( fmt );
  348. if ( numBones > 0 ) map |= LSB << ( VERTEX_ELEMENT_BONEWEIGHTS1 + numBones - 1 );
  349. int userDataSize = UserDataSize( fmt );
  350. if ( userDataSize > 0 ) map |= LSB << ( VERTEX_ELEMENT_USERDATA1 + userDataSize - 1 );
  351. for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i )
  352. {
  353. VertexElement_t texCoordElements[4] = { VERTEX_ELEMENT_TEXCOORD1D_0, VERTEX_ELEMENT_TEXCOORD2D_0, VERTEX_ELEMENT_TEXCOORD3D_0, VERTEX_ELEMENT_TEXCOORD4D_0 };
  354. int nCoordSize = TexCoordSize( i, fmt );
  355. if ( nCoordSize > 0 )
  356. {
  357. Assert( nCoordSize <= 4 );
  358. if ( nCoordSize <= 4 )
  359. {
  360. map |= LSB << ( texCoordElements[ nCoordSize - 1 ] + i );
  361. }
  362. }
  363. }
  364. if ( map == 0 )
  365. {
  366. if ( !isDynamic )
  367. {
  368. // We expect all (non-dynamic) VB allocs to specify a vertex format
  369. // Warning("[VBMEM] unknown vertex format\n");
  370. return 0;
  371. }
  372. }
  373. else
  374. {
  375. if ( vertexSize != 0 )
  376. {
  377. // Make sure elementTable above matches external computations of vertex size
  378. // FIXME: make this assert dependent on whether the current VB is compressed or not
  379. VertexCompressionType_t compressionType = CompressionType( fmt );
  380. bool isCompressedAlloc = ( compressionType == VERTEX_COMPRESSION_ON );
  381. // FIXME: once we've finalised which elements we're compressing for ship, update
  382. // elementTable to reflect that and re-enable this assert for compressed verts
  383. if ( !isCompressedAlloc )
  384. {
  385. Assert( vertexSize == ComputeVertexSize( map, fmt, isCompressedAlloc ) );
  386. }
  387. }
  388. }
  389. return map;
  390. }
  391. void CVBAllocTracker::UpdateElements( CounterData & data, VertexFormat_t fmt, int numVerts, int vertexSize,
  392. bool isDynamic, bool isCompressed )
  393. {
  394. VertexElementMap_t map = ComputeElementMap( fmt, vertexSize, isDynamic );
  395. if ( map != 0 )
  396. {
  397. for (int i = 0;i < VERTEX_ELEMENT_NUMELEMENTS;i++)
  398. {
  399. // Count vertices (get bytes from our elements table)
  400. VertexElementMap_t LSB = 1;
  401. if ( map & ( LSB << i ) )
  402. {
  403. if ( isCompressed )
  404. data.m_elementsCompressed[ i ] += numVerts;
  405. else
  406. data.m_elementsUncompressed[ i ] += numVerts;
  407. }
  408. }
  409. }
  410. }
  411. int CVBAllocTracker::ComputeAlignmentWastage( int bufferSize )
  412. {
  413. if ( !IsX360() )
  414. return 0;
  415. // VBs are 4KB-aligned on 360, so we waste thiiiiiis much:
  416. return ( ( 4096 - (bufferSize & 4095)) & 4095 );
  417. }
  418. void CVBAllocTracker::AddSaving( int & alreadySaved, int & yetToSave, const char *allocatorName, VertexElement_t element, Saving_t savingType )
  419. {
  420. UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, HashString( allocatorName ) );
  421. if ( handle != m_VBCountTable.InvalidHandle() )
  422. {
  423. CheckForElementTableUpdates( elementTable[ element ] );
  424. CounterData & counterData = m_VBCountTable.Element( handle );
  425. const ElementData & elementData = elementTable[ element ];
  426. int numVerts = counterData.m_vertCount;
  427. int numCompressed = counterData.m_elementsCompressed[ element ];
  428. int numUncompressed = counterData.m_elementsUncompressed[ element ];
  429. switch( savingType )
  430. {
  431. case SAVING_COMPRESSION:
  432. alreadySaved += numCompressed*( elementData.uncompressed - elementData.currentCompressed );
  433. yetToSave += numUncompressed*( elementData.uncompressed - elementData.currentCompressed );
  434. break;
  435. case SAVING_REMOVAL:
  436. alreadySaved += elementData.uncompressed*( numVerts - ( numUncompressed + numCompressed ) );
  437. yetToSave += numUncompressed*elementData.uncompressed + numCompressed*elementData.uncompressed;
  438. break;
  439. case SAVING_ALIGNMENT:
  440. yetToSave += counterData.m_paddingCount;
  441. break;
  442. default:
  443. Assert(0);
  444. break;
  445. }
  446. }
  447. }
  448. void CVBAllocTracker::SpewExpectedSavings( void )
  449. {
  450. int alreadySaved = 0, yetToSave = 0;
  451. // We have removed bone weights+indices from static props
  452. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_REMOVAL );
  453. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_BONEINDEX, SAVING_REMOVAL );
  454. // We have removed vertex colors from all models (color should only ever be in stream1, for static vertex lighting)
  455. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL );
  456. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL );
  457. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_COLOR, SAVING_REMOVAL );
  458. // We expect to compress texcoords (DONE: normals+tangents, boneweights) for all studiomdls
  459. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION );
  460. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION );
  461. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_NORMAL, SAVING_COMPRESSION );
  462. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION );
  463. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION );
  464. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_USERDATA4, SAVING_COMPRESSION );
  465. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION );
  466. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_static)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION );
  467. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_TEXCOORD2D_0, SAVING_COMPRESSION );
  468. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_BONEWEIGHTS1, SAVING_COMPRESSION );
  469. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (character)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_COMPRESSION );
  470. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_BONEWEIGHTS1, SAVING_COMPRESSION );
  471. AddSaving( alreadySaved, yetToSave, "R_StudioCreateStaticMeshes (prop_dynamic)", VERTEX_ELEMENT_BONEWEIGHTS2, SAVING_COMPRESSION );
  472. // UNDONE: compress bone weights for studiomdls? (issue: possible flex artifacts, but 2xSHORTN probably ok)
  473. // UNDONE: compress positions (+wrinkle) for studiomdls? (issue: possible flex artifacts)
  474. // UNDONE: disable tangents for non-bumped models (issue: forcedmaterialoverride support... don't think that needs tangents, though
  475. // however, if we use UBYTE4 normal+tangent encoding, removing tangents saves nothing)
  476. if ( IsX360() )
  477. {
  478. // We expect to avoid 4-KB-alignment wastage for color meshes, by allocating them
  479. // out of a single, shared VB and adding per-mesh offsets in vertex shaders
  480. AddSaving( alreadySaved, yetToSave, "CColorMeshData::CreateResource", VERTEX_ELEMENT_USERDATA4, SAVING_ALIGNMENT );
  481. }
  482. Msg("[VBMEM]\n");
  483. Msg("[VBMEM] Total expected memory saving by disabling/compressing vertex elements: %6.2f MB\n", yetToSave / ( 1024.0f*1024.0f ) );
  484. Msg("[VBMEM] ( total memory already saved: %6.2f MB)\n", alreadySaved / ( 1024.0f*1024.0f ) );
  485. Msg("[VBMEM] - compression of model texcoords, [DONE: normals+tangents, bone weights]\n" );
  486. Msg("[VBMEM] - avoidance of 4-KB alignment wastage for color meshes (on 360)\n" );
  487. Msg("[VBMEM] - [DONE: removal of unneeded bone weights+indices on models]\n" );
  488. Msg("[VBMEM]\n");
  489. }
  490. void CVBAllocTracker::UpdateData( const char * allocatorName, short allocatorKey, int bufferSize, VertexFormat_t fmt,
  491. int numVerts, int vertexSize, bool isDynamic, bool isCompressed )
  492. {
  493. UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorKey );
  494. if ( handle != m_VBCountTable.InvalidHandle() )
  495. {
  496. CounterData & data = m_VBCountTable.Element( handle );
  497. data.m_memCount += bufferSize;
  498. Assert( data.m_memCount >= 0 );
  499. data.m_vertCount += numVerts;
  500. Assert( data.m_vertCount >= 0 );
  501. data.m_paddingCount += ( bufferSize < 0 ? -1 : +1 )*ComputeAlignmentWastage( abs( bufferSize ) );
  502. UpdateElements( data, fmt, numVerts, vertexSize, isDynamic, isCompressed );
  503. }
  504. }
  505. const char * CVBAllocTracker::GetNameString( int allocatorKey )
  506. {
  507. UtlHashFixedHandle_t handle = GetCounterHandle( NULL, allocatorKey );
  508. if ( handle != m_VBCountTable.InvalidHandle() )
  509. {
  510. CounterData & data = m_VBCountTable.Element( handle );
  511. return data.m_AllocatorName;
  512. }
  513. return "null";
  514. }
  515. void CVBAllocTracker::SpewData( const char * allocatorName, short nameHash )
  516. {
  517. short allocatorHash = allocatorName ? HashString( allocatorName ) : nameHash;
  518. UtlHashFixedHandle_t handle = GetCounterHandle( allocatorName, allocatorHash );
  519. if ( handle != m_VBCountTable.InvalidHandle() )
  520. {
  521. CounterData & data = m_VBCountTable.Element( handle );
  522. if ( data.m_memCount > 0 )
  523. {
  524. Msg("[VBMEM] running mem usage: (%5.2f M-verts) %6.2f MB | '%s'\n",
  525. data.m_vertCount / ( 1024.0f*1024.0f ),
  526. data.m_memCount / ( 1024.0f*1024.0f ),
  527. data.m_AllocatorName );
  528. }
  529. if ( data.m_paddingCount > 0 )
  530. {
  531. Msg("[VBMEM] 4KB VB alignment wastage: %6.2f MB | '%s'\n",
  532. data.m_paddingCount / ( 1024.0f*1024.0f ),
  533. data.m_AllocatorName );
  534. }
  535. }
  536. }
  537. void CVBAllocTracker::SpewDataSometimes( int inc )
  538. {
  539. static int count = 0;
  540. if ( inc < 0 ) count += inc;
  541. Assert( count >= 0 );
  542. int period = mem_vballocspew.GetInt();
  543. if ( period >= 1 )
  544. {
  545. if ( ( count % period ) == 0 )
  546. {
  547. Msg( "[VBMEM] Status after %d VB allocs:\n", count );
  548. //#define ROUND_UP( _x_ ) ( ( ( _x_ ) + 31 ) & 31 )
  549. //Msg( "[VBMEM] Conservative estimate of mem used to track allocs: %d\n", 4096*ROUND_UP( 4 + sizeof( CUtlPtrLinkedList<AllocData> ) ) + count*ROUND_UP( sizeof( AllocData ) + 8 ) );
  550. SpewData( "total_static" );
  551. SpewData( "unknown" );
  552. }
  553. }
  554. if ( inc > 0 ) count += inc;
  555. }
  556. void CVBAllocTracker::DumpVBAllocs()
  557. {
  558. m_VBAllocMutex.Lock();
  559. Msg("[VBMEM] ----running totals----\n" );
  560. for ( int i = ( m_VBTableNameHashes.Count() - 1 ); i >= 0; i-- )
  561. {
  562. short nameHash = m_VBTableNameHashes.Element( i );
  563. SpewElements( NULL, nameHash );
  564. }
  565. Msg("[VBMEM]\n");
  566. Msg("[VBMEM] 'U' - original memory usage (all vertices uncompressed)\n" );
  567. Msg("[VBMEM] 'C' - current memory usage (some compression)\n" );
  568. Msg("[VBMEM] 'I' - ideal memory usage (all verts maximally compressed)\n" );
  569. Msg("[VBMEM] 'D' - difference between C and I (-> how much more compression could save)\n" );
  570. Msg("[VBMEM] 'W' - memory wasted due to 4-KB vertex buffer alignment\n" );
  571. Msg("[VBMEM]\n");
  572. for ( int i = ( m_VBTableNameHashes.Count() - 1 ); i >= 0; i-- )
  573. {
  574. short nameHash = m_VBTableNameHashes.Element( i );
  575. SpewData( NULL, nameHash );
  576. }
  577. SpewExpectedSavings();
  578. Msg("[VBMEM] ----running totals----\n" );
  579. m_VBAllocMutex.Unlock();
  580. }
  581. #endif // ENABLE_VB_ALLOC_TRACKER
  582. void CVBAllocTracker::CountVB( void * buffer, bool isDynamic, int bufferSize, int vertexSize, VertexFormat_t fmt )
  583. {
  584. #if ENABLE_VB_ALLOC_TRACKER
  585. m_VBAllocMutex.Lock();
  586. // Update VB memory counts for the relevant allocation type
  587. // (NOTE: we have 'unknown', 'dynamic' and 'total' counts)
  588. const char * allocatorName = ( m_MeshAllocatorName[0] == 0 ) ? "unknown" : m_MeshAllocatorName;
  589. if ( isDynamic ) allocatorName = "total_dynamic";
  590. int numVerts = ( vertexSize > 0 ) ? ( bufferSize / vertexSize ) : 0;
  591. short totalStaticKey = HashString( "total_static" );
  592. short key = HashString( allocatorName );
  593. bool isCompressed = ( VERTEX_COMPRESSION_NONE != CompressionType( fmt ) );
  594. if ( m_MeshAllocatorName[0] == 0 )
  595. {
  596. Warning("[VBMEM] unknown allocation!\n");
  597. }
  598. // Add to the VB memory counters
  599. TrackAlloc( buffer, bufferSize, fmt, numVerts, key );
  600. if ( !isDynamic )
  601. {
  602. // Keep dynamic allocs out of the total (dynamic VBs don't get compressed)
  603. UpdateData( "total_static", totalStaticKey, bufferSize, fmt, numVerts, vertexSize, isDynamic, isCompressed );
  604. }
  605. UpdateData( allocatorName, key, bufferSize, fmt, numVerts, vertexSize, isDynamic, isCompressed );
  606. if ( m_bSuperSpew )
  607. {
  608. // Spew every alloc
  609. Msg( "[VBMEM] VB-alloc | %6.2f MB | %s | %s\n", bufferSize / ( 1024.0f*1024.0f ), ( isDynamic ? "DYNamic" : " STAtic" ), allocatorName );
  610. SpewData( allocatorName );
  611. }
  612. SpewDataSometimes( +1 );
  613. m_VBAllocMutex.Unlock();
  614. #endif // ENABLE_VB_ALLOC_TRACKER
  615. }
  616. void CVBAllocTracker::UnCountVB( void * buffer )
  617. {
  618. #if ENABLE_VB_ALLOC_TRACKER
  619. m_VBAllocMutex.Lock();
  620. short totalKey = HashString( "total_static" );
  621. short dynamicKey = HashString( "total_dynamic" );
  622. int bufferSize;
  623. VertexFormat_t fmt;
  624. int numVerts;
  625. short key;
  626. // We have to store allocation data because the caller often doesn't know what it alloc'd :o/
  627. if ( KillAlloc( buffer, bufferSize, fmt, numVerts, key ) )
  628. {
  629. bool isCompressed = ( VERTEX_COMPRESSION_NONE != CompressionType( fmt ) );
  630. bool isDynamic = ( key == dynamicKey );
  631. // Subtract from the VB memory counters
  632. if ( !isDynamic )
  633. {
  634. UpdateData( NULL, totalKey, -bufferSize, fmt, -numVerts, 0, isDynamic, isCompressed );
  635. }
  636. UpdateData( NULL, key, -bufferSize, fmt, -numVerts, 0, isDynamic, isCompressed );
  637. const char * nameString = GetNameString( key );
  638. if ( m_bSuperSpew )
  639. {
  640. Msg( "[VBMEM] VB-free | %6.2f MB | %s | %s\n", bufferSize / ( 1024.0f*1024.0f ), ( isDynamic ? "DYNamic" : " STAtic" ), nameString );
  641. SpewData( nameString );
  642. }
  643. SpewDataSometimes( -1 );
  644. }
  645. m_VBAllocMutex.Unlock();
  646. #endif // ENABLE_VB_ALLOC_TRACKER
  647. }
  648. bool CVBAllocTracker::TrackMeshAllocations( const char * allocatorName )
  649. {
  650. #if ENABLE_VB_ALLOC_TRACKER
  651. // Tracks mesh allocations by name (set this before an alloc, clear it after)
  652. if ( m_MeshAllocatorName[ 0 ] )
  653. {
  654. return true;
  655. }
  656. m_VBAllocMutex.Lock();
  657. if ( allocatorName )
  658. {
  659. V_strncpy( m_MeshAllocatorName, allocatorName, MAX_ALLOCATOR_NAME_SIZE );
  660. }
  661. else
  662. {
  663. m_MeshAllocatorName[0] = 0;
  664. }
  665. m_VBAllocMutex.Unlock();
  666. #endif // ENABLE_VB_ALLOC_TRACKER
  667. return false;
  668. }
  669. #ifndef RETAIL
  670. static void CC_DumpVBMemAllocs()
  671. {
  672. #if ( ENABLE_VB_ALLOC_TRACKER == 0 )
  673. Warning( "ENABLE_VB_ALLOC_TRACKER must be 1 to enable VB mem alloc tracking\n");
  674. #else
  675. s_VBAllocTracker.DumpVBAllocs();
  676. #endif
  677. }
  678. static ConCommand mem_dumpvballocs( "mem_dumpvballocs", CC_DumpVBMemAllocs, "Dump VB memory allocation stats.", FCVAR_CHEAT );
  679. #endif // RETAIL