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.

1535 lines
41 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "datacache/idatacache.h"
  7. #if defined( POSIX ) && !defined( _PS3 )
  8. #include <malloc.h>
  9. #endif
  10. #include "tier0/vprof.h"
  11. #include "basetypes.h"
  12. #include "convar.h"
  13. #include "interface.h"
  14. #include "datamanager.h"
  15. #include "utlrbtree.h"
  16. #include "utlhash.h"
  17. #include "utlmap.h"
  18. #include "generichash.h"
  19. #include "filesystem.h"
  20. #include "datacache.h"
  21. #include "utlvector.h"
  22. #include "fmtstr.h"
  23. #if defined( _X360 )
  24. #include "xbox/xbox_console.h"
  25. #endif
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. #ifdef _PS3
  29. #include "tls_ps3.h"
  30. extern uint32 gMinAllocSize;
  31. #endif //_PS3
  32. //-----------------------------------------------------------------------------
  33. // Singleton
  34. //-----------------------------------------------------------------------------
  35. CDataCache g_DataCache;
  36. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CDataCache, IDataCache, DATACACHE_INTERFACE_VERSION, g_DataCache );
  37. //-----------------------------------------------------------------------------
  38. //
  39. // Data Cache class implemenations
  40. //
  41. //-----------------------------------------------------------------------------
  42. //-----------------------------------------------------------------------------
  43. // Console commands
  44. //-----------------------------------------------------------------------------
  45. static ConVar mem_force_flush( "mem_force_flush", "0", 0, "Force cache flush of unlocked resources on every alloc" );
  46. static ConVar mem_force_flush_section( "mem_force_flush_section", "", 0, "Cache section to restrict mem_force_flush" );
  47. static int g_iDontForceFlush;
  48. //-----------------------------------------------------------------------------
  49. // DataCacheItem_t
  50. //-----------------------------------------------------------------------------
  51. DEFINE_FIXEDSIZE_ALLOCATOR_MT( DataCacheItem_t, 4096/sizeof(DataCacheItem_t), CUtlMemoryPool::GROW_SLOW );
  52. void DataCacheItem_t::DestroyResource()
  53. {
  54. if ( pSection )
  55. {
  56. pSection->DiscardItemData( this, DC_AGE_DISCARD );
  57. }
  58. delete this;
  59. }
  60. //-----------------------------------------------------------------------------
  61. // CDataCacheSection
  62. //-----------------------------------------------------------------------------
  63. CDataCacheSection::CDataCacheSection( CDataCache *pSharedCache, IDataCacheClient *pClient, const char *pszName )
  64. : m_pClient( pClient ),
  65. m_LRU( pSharedCache->m_LRU ),
  66. m_mutex( pSharedCache->m_mutex ),
  67. m_pSharedCache( pSharedCache ),
  68. m_nFrameUnlockCounter( 0 ),
  69. m_options( 0 )
  70. {
  71. memset( m_FrameLocks, 0, sizeof( m_FrameLocks ) );
  72. memset( &m_status, 0, sizeof(m_status) );
  73. AssertMsg1( strlen(pszName) <= DC_MAX_CLIENT_NAME, "Cache client name too long \"%s\"", pszName );
  74. Q_strncpy( szName, pszName, sizeof(szName) );
  75. for ( int i = 0; i < DC_MAX_THREADS_FRAMELOCKED; i++ )
  76. {
  77. FrameLock_t *pFrameLock = new FrameLock_t;
  78. pFrameLock->m_iThread = i;
  79. m_FreeFrameLocks.Push( pFrameLock );
  80. }
  81. }
  82. CDataCacheSection::~CDataCacheSection()
  83. {
  84. FrameLock_t *pFrameLock;
  85. while ( ( pFrameLock = m_FreeFrameLocks.Pop() ) != NULL )
  86. {
  87. delete pFrameLock;
  88. }
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Controls cache size.
  92. //-----------------------------------------------------------------------------
  93. void CDataCacheSection::SetLimits( const DataCacheLimits_t &limits )
  94. {
  95. m_limits = limits;
  96. AssertMsg( m_limits.nMinBytes == 0 && m_limits.nMinItems == 0, "Cache minimums not yet implemented" );
  97. }
  98. //-----------------------------------------------------------------------------
  99. //
  100. //-----------------------------------------------------------------------------
  101. const DataCacheLimits_t &CDataCacheSection::GetLimits()
  102. {
  103. return m_limits;
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Controls cache options.
  107. //-----------------------------------------------------------------------------
  108. void CDataCacheSection::SetOptions( unsigned options )
  109. {
  110. m_options = options;
  111. }
  112. unsigned int CDataCacheSection::GetOptions()
  113. {
  114. return m_options;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Get the current state of the section
  118. //-----------------------------------------------------------------------------
  119. void CDataCacheSection::GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits )
  120. {
  121. if ( pStatus )
  122. {
  123. *pStatus = m_status;
  124. }
  125. if ( pLimits )
  126. {
  127. *pLimits = m_limits;
  128. }
  129. }
  130. //-----------------------------------------------------------------------------
  131. //
  132. //-----------------------------------------------------------------------------
  133. void CDataCacheSection::EnsureCapacity( unsigned nBytes, unsigned nItems )
  134. {
  135. VPROF( "CDataCacheSection::EnsureCapacity" );
  136. if ( m_limits.nMaxItems != (unsigned)-1 || m_limits.nMaxBytes != (unsigned)-1 )
  137. {
  138. unsigned nNewSectionBytes = GetNumBytes() + nBytes;
  139. if ( nNewSectionBytes > m_limits.nMaxBytes )
  140. {
  141. Purge( nNewSectionBytes - m_limits.nMaxBytes );
  142. }
  143. if ( GetNumItems() >= m_limits.nMaxItems )
  144. {
  145. PurgeItems( ( GetNumItems() - m_limits.nMaxItems ) + 1 );
  146. }
  147. }
  148. m_pSharedCache->EnsureCapacity( nBytes );
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache.
  152. //-----------------------------------------------------------------------------
  153. bool CDataCacheSection::Add( DataCacheClientID_t clientId, const void *pItemData, unsigned size, DataCacheHandle_t *pHandle )
  154. {
  155. return AddEx( clientId, pItemData, size, DCAF_DEFAULT, pHandle );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache.
  159. //-----------------------------------------------------------------------------
  160. bool CDataCacheSection::AddEx( DataCacheClientID_t clientId, const void *pItemData, unsigned size, unsigned flags, DataCacheHandle_t *pHandle )
  161. {
  162. VPROF( "CDataCacheSection::Add" );
  163. #if !defined( _CERT )
  164. ForceFlushDebug( true );
  165. #endif
  166. if ( ( m_options & DC_VALIDATE ) && Find( clientId ) )
  167. {
  168. Error( "Duplicate add to data cache\n" );
  169. return false;
  170. }
  171. EnsureCapacity( size );
  172. DataCacheItemData_t itemData =
  173. {
  174. pItemData,
  175. size,
  176. clientId,
  177. this
  178. };
  179. memhandle_t hMem = m_LRU.CreateResource( itemData, true );
  180. #ifdef _PS3
  181. if ( m_LRU.LockCount( hMem ) == 1 )
  182. {
  183. NoteLock( size );
  184. }
  185. #endif
  186. Assert( hMem != (memhandle_t)0 && hMem != (memhandle_t)DC_INVALID_HANDLE );
  187. AccessItem( hMem )->hLRU = hMem;
  188. if ( pHandle )
  189. {
  190. *pHandle = (DataCacheHandle_t)hMem;
  191. }
  192. NoteAdd( size );
  193. OnAdd( clientId, (DataCacheHandle_t)hMem );
  194. g_iDontForceFlush++;
  195. if ( flags & DCAF_LOCK )
  196. {
  197. Lock( (DataCacheHandle_t)hMem );
  198. }
  199. // Add implies a frame lock. A no-op if not in frame lock
  200. FrameLock( (DataCacheHandle_t)hMem );
  201. g_iDontForceFlush--;
  202. m_LRU.UnlockResource( hMem );
  203. return true;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose: Finds an item in the cache, returns NULL if item is not in cache.
  207. //-----------------------------------------------------------------------------
  208. DataCacheHandle_t CDataCacheSection::Find( DataCacheClientID_t clientId )
  209. {
  210. VPROF( "CDataCacheSection::Find" );
  211. m_status.nFindRequests++;
  212. DataCacheHandle_t hResult = DoFind( clientId );
  213. if ( hResult != DC_INVALID_HANDLE )
  214. {
  215. m_status.nFindHits++;
  216. }
  217. return hResult;
  218. }
  219. //---------------------------------------------------------
  220. DataCacheHandle_t CDataCacheSection::DoFind( DataCacheClientID_t clientId )
  221. {
  222. AUTO_LOCK( m_mutex );
  223. memhandle_t hCurrent;
  224. hCurrent = GetFirstUnlockedItem();
  225. while ( hCurrent != INVALID_MEMHANDLE )
  226. {
  227. if ( AccessItem( hCurrent )->clientId == clientId )
  228. {
  229. m_status.nFindHits++;
  230. return (DataCacheHandle_t)hCurrent;
  231. }
  232. hCurrent = GetNextItem( hCurrent );
  233. }
  234. hCurrent = GetFirstLockedItem();
  235. while ( hCurrent != INVALID_MEMHANDLE )
  236. {
  237. if ( AccessItem( hCurrent )->clientId == clientId )
  238. {
  239. m_status.nFindHits++;
  240. return (DataCacheHandle_t)hCurrent;
  241. }
  242. hCurrent = GetNextItem( hCurrent );
  243. }
  244. return DC_INVALID_HANDLE;
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose: Get an item out of the cache and remove it. No callbacks are executed.
  248. //-----------------------------------------------------------------------------
  249. DataCacheRemoveResult_t CDataCacheSection::Remove( DataCacheHandle_t handle, const void **ppItemData, unsigned *pItemSize, bool bNotify )
  250. {
  251. VPROF( "CDataCacheSection::Remove" );
  252. if ( handle != DC_INVALID_HANDLE )
  253. {
  254. memhandle_t lruHandle = (memhandle_t)handle;
  255. if ( m_LRU.LockCount( lruHandle ) > 0 )
  256. {
  257. return DC_LOCKED;
  258. }
  259. AUTO_LOCK( m_mutex );
  260. DataCacheItem_t *pItem = AccessItem( lruHandle );
  261. if ( pItem )
  262. {
  263. if ( ppItemData )
  264. {
  265. *ppItemData = pItem->pItemData;
  266. }
  267. if ( pItemSize )
  268. {
  269. *pItemSize = pItem->size;
  270. }
  271. DiscardItem( lruHandle, ( bNotify ) ? DC_REMOVED : DC_NONE );
  272. return DC_OK;
  273. }
  274. }
  275. return DC_NOT_FOUND;
  276. }
  277. //-----------------------------------------------------------------------------
  278. //
  279. //-----------------------------------------------------------------------------
  280. bool CDataCacheSection::IsPresent( DataCacheHandle_t handle )
  281. {
  282. return ( m_LRU.GetResource_NoLockNoLRUTouch( (memhandle_t)handle ) != NULL );
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose: Get without locking
  286. //-----------------------------------------------------------------------------
  287. void CDataCacheSection::GetAndLockMultiple( void **ppData, int nCount, DataCacheHandle_t *pHandles )
  288. {
  289. VPROF( "CDataCacheSection::GetAndLockMultiple" );
  290. #if !defined( _CERT )
  291. ForceFlushDebug( !g_iDontForceFlush );
  292. #endif
  293. AUTO_LOCK( m_mutex );
  294. for ( int i = 0; i < nCount; ++i )
  295. {
  296. if ( pHandles[i] == DC_INVALID_HANDLE )
  297. {
  298. ppData[i] = NULL;
  299. continue;
  300. }
  301. int nLockCount;
  302. DataCacheItem_t *pItem = m_LRU.LockResourceReturnCount( &nLockCount, (memhandle_t)pHandles[i] );
  303. if ( !pItem )
  304. {
  305. ppData[i] = NULL;
  306. continue;
  307. }
  308. if ( nLockCount == 1 )
  309. {
  310. NoteLock( pItem->size );
  311. }
  312. ppData[i] = const_cast<void *>( pItem->pItemData );
  313. }
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Lock an item in the cache, returns NULL if item is not in the cache.
  317. //-----------------------------------------------------------------------------
  318. void *CDataCacheSection::Lock( DataCacheHandle_t handle )
  319. {
  320. VPROF( "CDataCacheSection::Lock" );
  321. #if !defined( _CERT )
  322. ForceFlushDebug( !g_iDontForceFlush );
  323. #endif
  324. if ( handle != DC_INVALID_HANDLE )
  325. {
  326. int nCount;
  327. DataCacheItem_t *pItem = m_LRU.LockResourceReturnCount( &nCount, (memhandle_t)handle );
  328. if ( pItem )
  329. {
  330. if ( nCount == 1 )
  331. {
  332. NoteLock( pItem->size );
  333. }
  334. return const_cast<void *>(pItem->pItemData);
  335. }
  336. }
  337. return NULL;
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose: Unlock a previous lock.
  341. //-----------------------------------------------------------------------------
  342. int CDataCacheSection::Unlock( DataCacheHandle_t handle )
  343. {
  344. VPROF( "CDataCacheSection::Unlock" );
  345. int iNewLockCount = 0;
  346. if ( handle != DC_INVALID_HANDLE )
  347. {
  348. AssertMsg( AccessItem( (memhandle_t)handle ) != NULL, "Attempted to unlock nonexistent cache entry" );
  349. unsigned nBytesUnlocked = 0;
  350. m_mutex.Lock();
  351. iNewLockCount = m_LRU.UnlockResource( (memhandle_t)handle );
  352. if ( iNewLockCount == 0 )
  353. {
  354. nBytesUnlocked = AccessItem( (memhandle_t)handle )->size;
  355. }
  356. m_mutex.Unlock();
  357. if ( nBytesUnlocked )
  358. {
  359. NoteUnlock( nBytesUnlocked );
  360. EnsureCapacity( 0 );
  361. }
  362. }
  363. return iNewLockCount;
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: Lock the mutex
  367. //-----------------------------------------------------------------------------
  368. void CDataCacheSection::LockMutex()
  369. {
  370. g_iDontForceFlush++;
  371. m_mutex.Lock();
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose: Unlock the mutex
  375. //-----------------------------------------------------------------------------
  376. void CDataCacheSection::UnlockMutex()
  377. {
  378. g_iDontForceFlush--;
  379. m_mutex.Unlock();
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: Get without locking
  383. //-----------------------------------------------------------------------------
  384. void *CDataCacheSection::Get( DataCacheHandle_t handle, bool bFrameLock )
  385. {
  386. VPROF( "CDataCacheSection::Get" );
  387. #if !defined( _CERT )
  388. ForceFlushDebug( !g_iDontForceFlush );
  389. #endif
  390. if ( handle != DC_INVALID_HANDLE )
  391. {
  392. if ( bFrameLock && IsFrameLocking() )
  393. return FrameLock( handle );
  394. AUTO_LOCK( m_mutex );
  395. DataCacheItem_t *pItem = m_LRU.GetResource_NoLock( (memhandle_t)handle );
  396. if ( pItem )
  397. {
  398. return const_cast<void *>( pItem->pItemData );
  399. }
  400. }
  401. return NULL;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: Get without locking
  405. //-----------------------------------------------------------------------------
  406. void *CDataCacheSection::GetNoTouch( DataCacheHandle_t handle, bool bFrameLock )
  407. {
  408. VPROF( "CDataCacheSection::GetNoTouch" );
  409. if ( handle != DC_INVALID_HANDLE )
  410. {
  411. if ( bFrameLock && IsFrameLocking() )
  412. return FrameLock( handle );
  413. AUTO_LOCK( m_mutex );
  414. DataCacheItem_t *pItem = m_LRU.GetResource_NoLockNoLRUTouch( (memhandle_t)handle );
  415. if ( pItem )
  416. {
  417. return const_cast<void *>( pItem->pItemData );
  418. }
  419. }
  420. return NULL;
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Purpose: "Frame locking" (not game frame). A crude way to manage locks over relatively
  424. // short periods. Does not affect normal locks/unlocks
  425. //-----------------------------------------------------------------------------
  426. int CDataCacheSection::BeginFrameLocking()
  427. {
  428. int nThreadID = g_nThreadID;
  429. FrameLock_t *pFrameLock = m_FrameLocks[nThreadID];
  430. if ( pFrameLock )
  431. {
  432. pFrameLock->m_iLock++;
  433. }
  434. else
  435. {
  436. while ( ( pFrameLock = m_FreeFrameLocks.Pop() ) == NULL )
  437. {
  438. ThreadPause();
  439. ThreadSleep( 1 );
  440. }
  441. pFrameLock->m_iLock = 1;
  442. pFrameLock->m_pFirst = NULL;
  443. m_FrameLocks[nThreadID] = pFrameLock;
  444. }
  445. return pFrameLock->m_iLock;
  446. }
  447. //-----------------------------------------------------------------------------
  448. //
  449. //-----------------------------------------------------------------------------
  450. bool CDataCacheSection::IsFrameLocking()
  451. {
  452. FrameLock_t *pFrameLock = m_FrameLocks[g_nThreadID];
  453. return ( pFrameLock != NULL );
  454. }
  455. //-----------------------------------------------------------------------------
  456. //
  457. //-----------------------------------------------------------------------------
  458. void *CDataCacheSection::FrameLock( DataCacheHandle_t handle )
  459. {
  460. VPROF( "CDataCacheSection::FrameLock" );
  461. #if !defined( _CERT )
  462. ForceFlushDebug( !g_iDontForceFlush );
  463. #endif
  464. void *pResult = NULL;
  465. FrameLock_t *pFrameLock = m_FrameLocks[g_nThreadID];
  466. if ( pFrameLock )
  467. {
  468. DataCacheItem_t *pItem = m_LRU.LockResource( (memhandle_t)handle );
  469. if ( pItem )
  470. {
  471. int iThread = pFrameLock->m_iThread;
  472. if ( pItem->pNextFrameLocked[iThread] == DC_NO_NEXT_LOCKED )
  473. {
  474. pItem->pNextFrameLocked[iThread] = pFrameLock->m_pFirst;
  475. pFrameLock->m_pFirst = pItem;
  476. Lock( handle );
  477. }
  478. pResult = const_cast<void *>(pItem->pItemData);
  479. m_LRU.UnlockResource( (memhandle_t)handle );
  480. }
  481. }
  482. return pResult;
  483. }
  484. //-----------------------------------------------------------------------------
  485. //
  486. //-----------------------------------------------------------------------------
  487. int CDataCacheSection::EndFrameLocking()
  488. {
  489. int nThread = g_nThreadID;
  490. FrameLock_t *pFrameLock = m_FrameLocks[nThread];
  491. Assert( pFrameLock->m_iLock > 0 );
  492. if ( pFrameLock->m_iLock == 1 )
  493. {
  494. VPROF( "CDataCacheSection::EndFrameLocking" );
  495. if ( pFrameLock->m_pFirst )
  496. {
  497. AUTO_LOCK( m_mutex );
  498. DataCacheItem_t *pItem = pFrameLock->m_pFirst;
  499. DataCacheItem_t *pNext;
  500. int iThread = pFrameLock->m_iThread;
  501. while ( pItem )
  502. {
  503. pNext = pItem->pNextFrameLocked[iThread];
  504. pItem->pNextFrameLocked[iThread] = DC_NO_NEXT_LOCKED;
  505. Unlock( pItem->hLRU );
  506. pItem = pNext;
  507. }
  508. }
  509. m_FreeFrameLocks.Push( pFrameLock );
  510. m_FrameLocks[nThread] = NULL;
  511. return 0;
  512. }
  513. else
  514. {
  515. pFrameLock->m_iLock--;
  516. }
  517. return pFrameLock->m_iLock;
  518. }
  519. //-----------------------------------------------------------------------------
  520. //
  521. //-----------------------------------------------------------------------------
  522. int *CDataCacheSection::GetFrameUnlockCounterPtr()
  523. {
  524. return &m_nFrameUnlockCounter;
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Purpose: Lock management, not for the feint of heart
  528. //-----------------------------------------------------------------------------
  529. int CDataCacheSection::GetLockCount( DataCacheHandle_t handle )
  530. {
  531. return m_LRU.LockCount( (memhandle_t)handle );
  532. }
  533. //-----------------------------------------------------------------------------
  534. //
  535. //-----------------------------------------------------------------------------
  536. int CDataCacheSection::BreakLock( DataCacheHandle_t handle )
  537. {
  538. return m_LRU.BreakLock( (memhandle_t)handle );
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Purpose: Explicitly mark an item as "recently used"
  542. //-----------------------------------------------------------------------------
  543. bool CDataCacheSection::Touch( DataCacheHandle_t handle )
  544. {
  545. m_LRU.TouchResource( (memhandle_t)handle );
  546. return true;
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Purpose: Explicitly mark an item as "least recently used".
  550. //-----------------------------------------------------------------------------
  551. bool CDataCacheSection::Age( DataCacheHandle_t handle )
  552. {
  553. m_LRU.MarkAsStale( (memhandle_t)handle );
  554. return true;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified
  558. //-----------------------------------------------------------------------------
  559. unsigned CDataCacheSection::Flush( bool bUnlockedOnly, bool bNotify )
  560. {
  561. VPROF( "CDataCacheSection::Flush" );
  562. AUTO_LOCK( m_mutex );
  563. DataCacheNotificationType_t notificationType = ( bNotify )? DC_FLUSH_DISCARD : DC_NONE;
  564. memhandle_t hCurrent;
  565. memhandle_t hNext;
  566. unsigned nBytesFlushed = 0;
  567. unsigned nBytesCurrent = 0;
  568. hCurrent = GetFirstUnlockedItem();
  569. while ( hCurrent != INVALID_MEMHANDLE )
  570. {
  571. hNext = GetNextItem( hCurrent );
  572. nBytesCurrent = AccessItem( hCurrent )->size;
  573. if ( DiscardItem( hCurrent, notificationType ) )
  574. {
  575. nBytesFlushed += nBytesCurrent;
  576. }
  577. hCurrent = hNext;
  578. }
  579. if ( !bUnlockedOnly )
  580. {
  581. hCurrent = GetFirstLockedItem();
  582. while ( hCurrent != INVALID_MEMHANDLE )
  583. {
  584. hNext = GetNextItem( hCurrent );
  585. nBytesCurrent = AccessItem( hCurrent )->size;
  586. if ( DiscardItem( hCurrent, notificationType ) )
  587. {
  588. nBytesFlushed += nBytesCurrent;
  589. }
  590. hCurrent = hNext;
  591. }
  592. }
  593. return nBytesFlushed;
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed
  597. //-----------------------------------------------------------------------------
  598. unsigned CDataCacheSection::Purge( unsigned nBytes )
  599. {
  600. VPROF( "CDataCacheSection::Purge" );
  601. AUTO_LOCK( m_mutex );
  602. unsigned nBytesPurged = 0;
  603. unsigned nBytesCurrent = 0;
  604. memhandle_t hCurrent = GetFirstUnlockedItem();
  605. memhandle_t hNext;
  606. while ( hCurrent != INVALID_MEMHANDLE && nBytes > 0 )
  607. {
  608. bool bFree = true;
  609. hNext = GetNextItem( hCurrent );
  610. if(bFree)
  611. {
  612. nBytesCurrent = AccessItem( hCurrent )->size;
  613. if ( DiscardItem( hCurrent, DC_FLUSH_DISCARD ) )
  614. {
  615. nBytesPurged += nBytesCurrent;
  616. nBytes -= MIN( nBytesCurrent, nBytes );
  617. }
  618. }
  619. hCurrent = hNext;
  620. }
  621. return nBytesPurged;
  622. }
  623. //-----------------------------------------------------------------------------
  624. // Purpose: Dump the oldest items to free the specified number of items. Returns number actually freed
  625. //-----------------------------------------------------------------------------
  626. unsigned CDataCacheSection::PurgeItems( unsigned nItems )
  627. {
  628. AUTO_LOCK( m_mutex );
  629. unsigned nPurged = 0;
  630. memhandle_t hCurrent = GetFirstUnlockedItem();
  631. memhandle_t hNext;
  632. while ( hCurrent != INVALID_MEMHANDLE && nItems )
  633. {
  634. hNext = GetNextItem( hCurrent );
  635. if ( DiscardItem( hCurrent, DC_FLUSH_DISCARD ) )
  636. {
  637. nItems--;
  638. nPurged++;
  639. }
  640. hCurrent = hNext;
  641. }
  642. return nPurged;
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Purpose: Output the state of the section
  646. //-----------------------------------------------------------------------------
  647. void CDataCacheSection::OutputReport( DataCacheReportType_t reportType )
  648. {
  649. m_pSharedCache->OutputReport( reportType, GetName() );
  650. }
  651. //-----------------------------------------------------------------------------
  652. // Purpose: Updates the size of a specific item
  653. // Input : handle -
  654. // newSize -
  655. //-----------------------------------------------------------------------------
  656. void CDataCacheSection::UpdateSize( DataCacheHandle_t handle, unsigned int nNewSize )
  657. {
  658. DataCacheItem_t *pItem = m_LRU.LockResource( (memhandle_t)handle );
  659. if ( !pItem )
  660. {
  661. // If it's gone from memory, size is already irrelevant
  662. return;
  663. }
  664. unsigned oldSize = pItem->size;
  665. if ( oldSize != nNewSize )
  666. {
  667. // Update the size
  668. pItem->size = nNewSize;
  669. int bytesAdded = nNewSize - oldSize;
  670. // If change would grow cache size, then purge items until we have room
  671. if ( bytesAdded > 0 )
  672. {
  673. m_pSharedCache->EnsureCapacity( bytesAdded );
  674. }
  675. m_LRU.NotifySizeChanged( (memhandle_t)handle, oldSize, nNewSize );
  676. NoteSizeChanged( oldSize, nNewSize );
  677. }
  678. m_LRU.UnlockResource( (memhandle_t)handle );
  679. }
  680. //-----------------------------------------------------------------------------
  681. //
  682. //-----------------------------------------------------------------------------
  683. memhandle_t CDataCacheSection::GetFirstUnlockedItem()
  684. {
  685. memhandle_t hCurrent;
  686. hCurrent = m_LRU.GetFirstUnlocked();
  687. while ( hCurrent != INVALID_MEMHANDLE )
  688. {
  689. if ( AccessItem( hCurrent )->pSection == this )
  690. {
  691. return hCurrent;
  692. }
  693. hCurrent = m_LRU.GetNext( hCurrent );
  694. }
  695. return INVALID_MEMHANDLE;
  696. }
  697. memhandle_t CDataCacheSection::GetFirstLockedItem()
  698. {
  699. memhandle_t hCurrent;
  700. hCurrent = m_LRU.GetFirstLocked();
  701. while ( hCurrent != INVALID_MEMHANDLE )
  702. {
  703. if ( AccessItem( hCurrent )->pSection == this )
  704. {
  705. return hCurrent;
  706. }
  707. hCurrent = m_LRU.GetNext( hCurrent );
  708. }
  709. return INVALID_MEMHANDLE;
  710. }
  711. memhandle_t CDataCacheSection::GetNextItem( memhandle_t hCurrent )
  712. {
  713. hCurrent = m_LRU.GetNext( hCurrent );
  714. while ( hCurrent != INVALID_MEMHANDLE )
  715. {
  716. if ( AccessItem( hCurrent )->pSection == this )
  717. {
  718. return hCurrent;
  719. }
  720. hCurrent = m_LRU.GetNext( hCurrent );
  721. }
  722. return INVALID_MEMHANDLE;
  723. }
  724. bool CDataCacheSection::DiscardItem( memhandle_t hItem, DataCacheNotificationType_t type )
  725. {
  726. DataCacheItem_t *pItem = AccessItem( hItem );
  727. if ( DiscardItemData( pItem, type ) )
  728. {
  729. if ( m_LRU.LockCount( hItem ) )
  730. {
  731. m_LRU.BreakLock( hItem );
  732. NoteUnlock( pItem->size );
  733. }
  734. FrameLock_t *pFrameLock = m_FrameLocks[g_nThreadID];
  735. if ( pFrameLock )
  736. {
  737. int iThread = pFrameLock->m_iThread;
  738. if ( pItem->pNextFrameLocked[iThread] != DC_NO_NEXT_LOCKED )
  739. {
  740. if ( pFrameLock->m_pFirst == pItem )
  741. {
  742. pFrameLock->m_pFirst = pItem->pNextFrameLocked[iThread];
  743. }
  744. else
  745. {
  746. DataCacheItem_t *pCurrent = pFrameLock->m_pFirst;
  747. while ( pCurrent )
  748. {
  749. if ( pCurrent->pNextFrameLocked[iThread] == pItem )
  750. {
  751. pCurrent->pNextFrameLocked[iThread] = pItem->pNextFrameLocked[iThread];
  752. break;
  753. }
  754. pCurrent = pCurrent->pNextFrameLocked[iThread];
  755. }
  756. }
  757. pItem->pNextFrameLocked[iThread] = DC_NO_NEXT_LOCKED;
  758. }
  759. }
  760. #ifdef _DEBUG
  761. for ( int i = 0; i < DC_MAX_THREADS_FRAMELOCKED; i++ )
  762. {
  763. if ( pItem->pNextFrameLocked[i] != DC_NO_NEXT_LOCKED )
  764. {
  765. DebuggerBreak(); // higher level code needs to handle better
  766. }
  767. }
  768. #endif
  769. pItem->pSection = NULL; // inhibit callbacks from lower level resource system
  770. m_LRU.DestroyResource( hItem );
  771. return true;
  772. }
  773. return false;
  774. }
  775. bool CDataCacheSection::DiscardItemData( DataCacheItem_t *pItem, DataCacheNotificationType_t type )
  776. {
  777. if ( pItem )
  778. {
  779. if ( type != DC_NONE )
  780. {
  781. Assert( type == DC_AGE_DISCARD || type == DC_FLUSH_DISCARD || DC_REMOVED );
  782. if ( type == DC_AGE_DISCARD && m_pSharedCache->IsInFlush() )
  783. type = DC_FLUSH_DISCARD;
  784. DataCacheNotification_t notification =
  785. {
  786. type,
  787. GetName(),
  788. pItem->clientId,
  789. pItem->pItemData,
  790. pItem->size
  791. };
  792. bool bResult = m_pClient->HandleCacheNotification( notification );
  793. AssertMsg( bResult, "Refusal of cache drop not yet implemented!" );
  794. if ( bResult )
  795. {
  796. NoteRemove( pItem->size );
  797. }
  798. return bResult;
  799. }
  800. OnRemove( pItem->clientId );
  801. pItem->pSection = NULL;
  802. pItem->pItemData = NULL,
  803. pItem->clientId = 0;
  804. NoteRemove( pItem->size );
  805. return true;
  806. }
  807. return false;
  808. }
  809. void CDataCacheSection::ForceFlushDebug( bool bFlush )
  810. {
  811. if ( bFlush && mem_force_flush.GetBool() )
  812. {
  813. if ( !*mem_force_flush_section.GetString() )
  814. {
  815. if ( IsGameConsole() )
  816. {
  817. // The 360 does not use LRU purge behavior on some sections (section limits are -1) and thus cannot handle arbitrary purges.
  818. // Instead the 360 marks those sections, and then must iterate/skip here
  819. int count = m_pSharedCache->GetSectionCount();
  820. for ( int i = 0; i < count; i++ )
  821. {
  822. IDataCacheSection *pCacheSection = m_pSharedCache->FindSection( m_pSharedCache->GetSectionName( i ) );
  823. if ( pCacheSection && !( pCacheSection->GetOptions() & DC_NO_USER_FORCE_FLUSH ) )
  824. {
  825. pCacheSection->Flush();
  826. }
  827. }
  828. }
  829. else
  830. {
  831. m_pSharedCache->Flush();
  832. }
  833. }
  834. else if ( stricmp( szName, mem_force_flush_section.GetString() ) == 0 )
  835. {
  836. // allowing user to ignore safety barrier and force flush
  837. Flush();
  838. }
  839. }
  840. }
  841. //-----------------------------------------------------------------------------
  842. // CDataCacheSectionFastFind
  843. //-----------------------------------------------------------------------------
  844. DataCacheHandle_t CDataCacheSectionFastFind::DoFind( DataCacheClientID_t clientId )
  845. {
  846. AUTO_LOCK( m_mutex );
  847. UtlHashFastHandle_t hHash = m_Handles.Find( Hash4( &clientId ) );
  848. if( hHash != m_Handles.InvalidHandle() )
  849. return m_Handles[hHash];
  850. return DC_INVALID_HANDLE;
  851. }
  852. void CDataCacheSectionFastFind::OnAdd( DataCacheClientID_t clientId, DataCacheHandle_t hCacheItem )
  853. {
  854. AUTO_LOCK( m_mutex );
  855. Assert( m_Handles.Find( Hash4( &clientId ) ) == m_Handles.InvalidHandle());
  856. m_Handles.FastInsert( Hash4( &clientId ), hCacheItem );
  857. }
  858. void CDataCacheSectionFastFind::OnRemove( DataCacheClientID_t clientId )
  859. {
  860. AUTO_LOCK( m_mutex );
  861. UtlHashFastHandle_t hHash = m_Handles.Find( Hash4( &clientId ) );
  862. Assert( hHash != m_Handles.InvalidHandle());
  863. if( hHash != m_Handles.InvalidHandle() )
  864. return m_Handles.Remove( hHash );
  865. }
  866. //-----------------------------------------------------------------------------
  867. // CDataCache
  868. //-----------------------------------------------------------------------------
  869. //-----------------------------------------------------------------------------
  870. // Convar callback to change data cache
  871. //-----------------------------------------------------------------------------
  872. void DataCacheSize_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  873. {
  874. ConVarRef var( pConVar );
  875. int nOldValue = (int)flOldValue;
  876. if ( var.GetInt() != nOldValue )
  877. {
  878. g_DataCache.SetSize( var.GetInt() * 1024 * 1024 );
  879. }
  880. }
  881. ConVar datacachesize( "datacachesize", "32", 0, "Size in MB.", true, 0, true, 128, DataCacheSize_f );
  882. //-----------------------------------------------------------------------------
  883. // Connect, disconnect
  884. //-----------------------------------------------------------------------------
  885. bool CDataCache::Connect( CreateInterfaceFn factory )
  886. {
  887. if ( !BaseClass::Connect( factory ) )
  888. return false;
  889. g_DataCache.SetSize( datacachesize.GetInt() * 1024 * 1024 );
  890. g_pDataCache = this;
  891. return true;
  892. }
  893. void CDataCache::Disconnect()
  894. {
  895. g_pDataCache = NULL;
  896. BaseClass::Disconnect();
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Init, Shutdown
  900. //-----------------------------------------------------------------------------
  901. InitReturnVal_t CDataCache::Init( void )
  902. {
  903. return BaseClass::Init();
  904. }
  905. void CDataCache::Shutdown( void )
  906. {
  907. Flush( false, false );
  908. BaseClass::Shutdown();
  909. }
  910. //-----------------------------------------------------------------------------
  911. // Query interface
  912. //-----------------------------------------------------------------------------
  913. void *CDataCache::QueryInterface( const char *pInterfaceName )
  914. {
  915. // Loading the datacache DLL mounts *all* interfaces
  916. // This includes the backward-compatible interfaces + IStudioDataCache
  917. CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
  918. return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
  919. }
  920. //-----------------------------------------------------------------------------
  921. //
  922. //-----------------------------------------------------------------------------
  923. CDataCache::CDataCache()
  924. : m_mutex( m_LRU.AccessMutex() )
  925. {
  926. memset( &m_status, 0, sizeof(m_status) );
  927. m_bInFlush = false;
  928. m_LRU.SetFreeOnDestruct( false ); // Causes problems in error shut down scenarios as CDataCache::Shutdown() isn't called, so the LRU is pointing to things owned by unloaded DLLs
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Purpose: Controls cache size.
  932. //-----------------------------------------------------------------------------
  933. void CDataCache::SetSize( int nMaxBytes )
  934. {
  935. m_LRU.SetTargetSize( nMaxBytes );
  936. m_LRU.FlushToTargetSize();
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose: Controls cache options.
  940. //-----------------------------------------------------------------------------
  941. void CDataCache::SetOptions( unsigned options )
  942. {
  943. for ( int i = 0; m_Sections.Count(); i++ )
  944. {
  945. m_Sections[i]->SetOptions( options );
  946. }
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose: Controls cache section size.
  950. //-----------------------------------------------------------------------------
  951. void CDataCache::SetSectionLimits( const char *pszSectionName, const DataCacheLimits_t &limits )
  952. {
  953. IDataCacheSection *pSection = FindSection( pszSectionName );
  954. if ( !pSection )
  955. {
  956. DevMsg( "Cannot find requested cache section \"%s\"", pszSectionName );
  957. return;
  958. }
  959. pSection->SetLimits( limits );
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Purpose: Get the current state of the cache
  963. //-----------------------------------------------------------------------------
  964. void CDataCache::GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits )
  965. {
  966. if ( pStatus )
  967. {
  968. *pStatus = m_status;
  969. }
  970. if ( pLimits )
  971. {
  972. Construct( pLimits );
  973. pLimits->nMaxBytes = m_LRU.TargetSize();
  974. }
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Purpose: Add a section to the cache
  978. //-----------------------------------------------------------------------------
  979. IDataCacheSection *CDataCache::AddSection( IDataCacheClient *pClient, const char *pszSectionName, const DataCacheLimits_t &limits, bool bSupportFastFind )
  980. {
  981. CDataCacheSection *pSection;
  982. pSection = (CDataCacheSection *)FindSection( pszSectionName );
  983. if ( pSection )
  984. {
  985. AssertMsg1( pSection->GetClient() == pClient, "Duplicate cache section name \"%s\"", pszSectionName );
  986. return pSection;
  987. }
  988. if ( !bSupportFastFind )
  989. pSection = new CDataCacheSection( this, pClient, pszSectionName );
  990. else
  991. pSection = new CDataCacheSectionFastFind( this, pClient, pszSectionName );
  992. pSection->SetLimits( limits );
  993. m_Sections.AddToTail( pSection );
  994. return pSection;
  995. }
  996. //-----------------------------------------------------------------------------
  997. // Purpose: Remove a section from the cache
  998. //-----------------------------------------------------------------------------
  999. void CDataCache::RemoveSection( const char *pszClientName, bool bCallFlush )
  1000. {
  1001. #ifdef _PS3
  1002. // TODO: if (!g_bDoingExitSequence)
  1003. #endif
  1004. {
  1005. int iSection = FindSectionIndex( pszClientName );
  1006. if ( iSection != m_Sections.InvalidIndex() )
  1007. {
  1008. if ( bCallFlush )
  1009. {
  1010. m_Sections[iSection]->Flush( false );
  1011. }
  1012. delete m_Sections[iSection];
  1013. m_Sections.FastRemove( iSection );
  1014. return;
  1015. }
  1016. }
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. // Purpose: Find a section of the cache
  1020. //-----------------------------------------------------------------------------
  1021. IDataCacheSection *CDataCache::FindSection( const char *pszClientName )
  1022. {
  1023. int iSection = FindSectionIndex( pszClientName );
  1024. if ( iSection != m_Sections.InvalidIndex() )
  1025. {
  1026. return m_Sections[iSection];
  1027. }
  1028. return NULL;
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. //
  1032. //-----------------------------------------------------------------------------
  1033. void CDataCache::EnsureCapacity( unsigned nBytes )
  1034. {
  1035. VPROF( "CDataCache::EnsureCapacity" );
  1036. m_LRU.EnsureCapacity( nBytes );
  1037. }
  1038. //-----------------------------------------------------------------------------
  1039. // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed
  1040. //-----------------------------------------------------------------------------
  1041. unsigned CDataCache::Purge( unsigned nBytes )
  1042. {
  1043. VPROF( "CDataCache::Purge" );
  1044. return m_LRU.Purge( nBytes );
  1045. }
  1046. //-----------------------------------------------------------------------------
  1047. // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified
  1048. //-----------------------------------------------------------------------------
  1049. unsigned CDataCache::Flush( bool bUnlockedOnly, bool bNotify )
  1050. {
  1051. VPROF( "CDataCache::Flush" );
  1052. unsigned result;
  1053. if ( m_bInFlush )
  1054. {
  1055. return 0;
  1056. }
  1057. m_bInFlush = true;
  1058. if ( bUnlockedOnly )
  1059. {
  1060. result = m_LRU.FlushAllUnlocked();
  1061. }
  1062. else
  1063. {
  1064. result = m_LRU.FlushAll();
  1065. }
  1066. m_bInFlush = false;
  1067. return result;
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Purpose: Output the state of the cache
  1071. //-----------------------------------------------------------------------------
  1072. void CDataCache::OutputReport( DataCacheReportType_t reportType, const char *pszSection )
  1073. {
  1074. float percent;
  1075. int i;
  1076. AUTO_LOCK( m_mutex );
  1077. int bytesUsed = m_LRU.UsedSize();
  1078. int bytesTotal = m_LRU.TargetSize();
  1079. percent = 100.0f * (float)bytesUsed / (float)bytesTotal;
  1080. CUtlVector<memhandle_t> lruList, lockedlist;
  1081. m_LRU.GetLockHandleList( lockedlist );
  1082. m_LRU.GetLRUHandleList( lruList );
  1083. CDataCacheSection *pSection = NULL;
  1084. if ( pszSection )
  1085. {
  1086. pSection = (CDataCacheSection *)FindSection( pszSection );
  1087. if ( !pSection )
  1088. {
  1089. Msg( "Unknown cache section %s\n", pszSection );
  1090. return;
  1091. }
  1092. }
  1093. if ( reportType == DC_DETAIL_REPORT )
  1094. {
  1095. CUtlRBTree< memhandle_t, int > sortedbysize( 0, 0, SortMemhandlesBySizeLessFunc );
  1096. for ( i = 0; i < lockedlist.Count(); ++i )
  1097. {
  1098. if ( !pSection || AccessItem( lockedlist[ i ] )->pSection == pSection )
  1099. sortedbysize.Insert( lockedlist[ i ] );
  1100. }
  1101. for ( i = 0; i < lruList.Count(); ++i )
  1102. {
  1103. if ( !pSection || AccessItem( lruList[ i ] )->pSection == pSection )
  1104. sortedbysize.Insert( lruList[ i ] );
  1105. }
  1106. for ( i = sortedbysize.FirstInorder(); i != sortedbysize.InvalidIndex(); i = sortedbysize.NextInorder( i ) )
  1107. {
  1108. OutputItemReport( sortedbysize[ i ] );
  1109. }
  1110. OutputReport( DC_SUMMARY_REPORT, pszSection );
  1111. }
  1112. else if ( reportType == DC_DETAIL_REPORT_LRU )
  1113. {
  1114. for ( i = 0; i < lockedlist.Count(); ++i )
  1115. {
  1116. if ( !pSection || AccessItem( lockedlist[ i ] )->pSection == pSection )
  1117. OutputItemReport( lockedlist[ i ] );
  1118. }
  1119. for ( i = 0; i < lruList.Count(); ++i )
  1120. {
  1121. if ( !pSection || AccessItem( lruList[ i ] )->pSection == pSection )
  1122. OutputItemReport( lruList[ i ] );
  1123. }
  1124. OutputReport( DC_SUMMARY_REPORT, pszSection );
  1125. }
  1126. #if defined( _X360 )
  1127. else if ( reportType == DC_DETAIL_REPORT_VXCONSOLE )
  1128. {
  1129. int numLockedItems = lockedlist.Count();
  1130. int numLruItems = lruList.Count();
  1131. CUtlVector< xDataCacheItem_t > vxconsoleItems;
  1132. vxconsoleItems.SetCount( numLockedItems + numLruItems );
  1133. for ( i = 0; i < numLockedItems; ++i )
  1134. {
  1135. OutputItemReport( lockedlist[i], &vxconsoleItems[i] );
  1136. }
  1137. for ( i = 0; i < numLruItems; ++i )
  1138. {
  1139. OutputItemReport( lruList[i], &vxconsoleItems[numLockedItems + i] );
  1140. }
  1141. XBX_rDataCacheList( vxconsoleItems.Count(), vxconsoleItems.Base() );
  1142. }
  1143. #endif
  1144. else if ( reportType == DC_SUMMARY_REPORT )
  1145. {
  1146. if ( !pszSection )
  1147. {
  1148. // summary for all of the sections
  1149. for ( int i = 0; i < m_Sections.Count(); ++i )
  1150. {
  1151. if ( m_Sections[i]->GetName() )
  1152. {
  1153. OutputReport( DC_SUMMARY_REPORT, m_Sections[i]->GetName() );
  1154. }
  1155. }
  1156. Msg( "Summary: %i resources total %s, %.2f %% of capacity\n", lockedlist.Count() + lruList.Count(), Q_pretifymem( bytesUsed, 2, true ), percent );
  1157. }
  1158. else
  1159. {
  1160. // summary for the specified section
  1161. DataCacheItem_t *pItem;
  1162. int sectionBytes = 0;
  1163. int sectionCount = 0;
  1164. for ( i = 0; i < lockedlist.Count(); ++i )
  1165. {
  1166. if ( AccessItem( lockedlist[ i ] )->pSection == pSection )
  1167. {
  1168. pItem = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lockedlist[i] );
  1169. sectionBytes += pItem->size;
  1170. sectionCount++;
  1171. }
  1172. }
  1173. for ( i = 0; i < lruList.Count(); ++i )
  1174. {
  1175. if ( AccessItem( lruList[ i ] )->pSection == pSection )
  1176. {
  1177. pItem = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lruList[i] );
  1178. sectionBytes += pItem->size;
  1179. sectionCount++;
  1180. }
  1181. }
  1182. int sectionSize = 1;
  1183. float sectionPercent;
  1184. if ( pSection->GetLimits().nMaxBytes == (unsigned int)-1 )
  1185. {
  1186. // section unrestricted, base on total size
  1187. sectionSize = bytesTotal;
  1188. }
  1189. else if ( pSection->GetLimits().nMaxBytes )
  1190. {
  1191. sectionSize = pSection->GetLimits().nMaxBytes;
  1192. }
  1193. sectionPercent = 100.0f * (float)sectionBytes/(float)sectionSize;
  1194. Msg( "Section [%s]: %i resources total %s, %.2f %% of limit (%s)\n", pszSection, sectionCount, Q_pretifymem( sectionBytes, 2, true ), sectionPercent, Q_pretifymem( sectionSize, 2, true ) );
  1195. }
  1196. }
  1197. }
  1198. //-------------------------------------
  1199. void CDataCache::OutputItemReport( memhandle_t hItem, void *pXboxData )
  1200. {
  1201. AUTO_LOCK( m_mutex );
  1202. DataCacheItem_t *pItem = m_LRU.GetResource_NoLockNoLRUTouch( hItem );
  1203. if ( !pItem )
  1204. return;
  1205. CDataCacheSection *pSection = pItem->pSection;
  1206. char name[DC_MAX_ITEM_NAME+1];
  1207. name[0] = 0;
  1208. pSection->GetClient()->GetItemName( pItem->clientId, pItem->pItemData, name, DC_MAX_ITEM_NAME );
  1209. #if defined( _X360 )
  1210. if ( pXboxData )
  1211. {
  1212. // spew into vxconsole friendly structure
  1213. xDataCacheItem_t *pXboxItem = (xDataCacheItem_t *)pXboxData;
  1214. V_strncpy( pXboxItem->name, name, sizeof( pXboxItem->section ) );
  1215. V_strncpy( pXboxItem->section, pItem->pSection->GetName(), sizeof( pXboxItem->section ) );
  1216. pXboxItem->size = pItem->size;
  1217. pXboxItem->lockCount = m_LRU.LockCount( hItem );
  1218. pXboxItem->clientId = pItem->clientId;
  1219. pXboxItem->itemData = (unsigned int)pItem->pItemData;
  1220. pXboxItem->handle = (unsigned int)hItem;
  1221. return;
  1222. }
  1223. #endif
  1224. Msg( "\t%16.16s : %12s : 0x%08x, %p, 0x%p : %s : %s\n",
  1225. Q_pretifymem( pItem->size, 2, true ),
  1226. pSection->GetName(),
  1227. pItem->clientId, pItem->pItemData, hItem,
  1228. ( name[0] ) ? name : "unknown",
  1229. ( m_LRU.LockCount( hItem ) ) ? CFmtStr( "Locked %d", m_LRU.LockCount( hItem ) ).operator const char*() : "" );
  1230. }
  1231. //-----------------------------------------------------------------------------
  1232. //
  1233. //-----------------------------------------------------------------------------
  1234. int CDataCache::FindSectionIndex( const char *pszSection )
  1235. {
  1236. for ( int i = 0; i < m_Sections.Count(); i++ )
  1237. {
  1238. if ( stricmp( m_Sections[i]->GetName(), pszSection ) == 0 )
  1239. return i;
  1240. }
  1241. return m_Sections.InvalidIndex();
  1242. }
  1243. //-----------------------------------------------------------------------------
  1244. // Sorting utility used by the data cache report
  1245. //-----------------------------------------------------------------------------
  1246. bool CDataCache::SortMemhandlesBySizeLessFunc( const memhandle_t& lhs, const memhandle_t& rhs )
  1247. {
  1248. DataCacheItem_t *pItem1 = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( lhs );
  1249. DataCacheItem_t *pItem2 = g_DataCache.m_LRU.GetResource_NoLockNoLRUTouch( rhs );
  1250. Assert( pItem1 );
  1251. Assert( pItem2 );
  1252. return pItem1->size < pItem2->size;
  1253. }
  1254. int CDataCache::GetSectionCount( void )
  1255. {
  1256. return m_Sections.Count();
  1257. }
  1258. const char *CDataCache::GetSectionName( int iIndex )
  1259. {
  1260. if ( iIndex >= 0 && iIndex < m_Sections.Count() )
  1261. {
  1262. return m_Sections[iIndex]->GetName();
  1263. }
  1264. return "";
  1265. }