Team Fortress 2 Source Code as on 22/4/2020
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.

764 lines
34 KiB

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