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.

1258 lines
30 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "tier1/mempool.h"
  7. #include "tier1/convar.h"
  8. #include "tier1/utlmap.h"
  9. #include "shaderapidx8.h"
  10. #include "texturedx8.h"
  11. #include "textureheap.h"
  12. #include "shaderapidx8_global.h"
  13. #include "tier0/memdbgon.h"
  14. #define USE_STANDARD_ALLOCATOR
  15. #ifdef USE_STANDARD_ALLOCATOR
  16. #define UseStandardAllocator() (true)
  17. #elif !defined(_RETAIL)
  18. bool g_bUseStandardAllocator = false;
  19. bool UseStandardAllocator()
  20. {
  21. static bool bReadCommandLine;
  22. if ( !bReadCommandLine )
  23. {
  24. bReadCommandLine = true;
  25. const char *pStr = Plat_GetCommandLine();
  26. if ( pStr )
  27. {
  28. char tempStr[512];
  29. Q_strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
  30. tempStr[ sizeof( tempStr ) - 1 ] = 0;
  31. _strlwr( tempStr );
  32. if ( strstr( tempStr, "-notextureheap" ) )
  33. g_bUseStandardAllocator = true;
  34. }
  35. }
  36. return g_bUseStandardAllocator;
  37. }
  38. #else
  39. #define UseStandardAllocator() (false)
  40. #endif
  41. #if !defined( _RELEASE ) && !defined( _RETAIL )
  42. #define StrongAssert( expr ) if ( (expr) ) ; else { DebuggerBreak(); }
  43. #else
  44. #define StrongAssert( expr ) ((void)0)
  45. #endif
  46. //-----------------------------------------------------------------------------
  47. // Get Texture HW base
  48. //-----------------------------------------------------------------------------
  49. void *GetD3DTextureBasePtr( IDirect3DBaseTexture* pTex )
  50. {
  51. // assumes base and mips are contiguous
  52. return (void *)( (unsigned int)pTex->Format.BaseAddress << 12 );
  53. }
  54. class CD3DTextureAllocator
  55. {
  56. public:
  57. static void *Alloc( int bytes )
  58. {
  59. DWORD attributes = MAKE_XALLOC_ATTRIBUTES(
  60. 0,
  61. false,
  62. TRUE,
  63. FALSE,
  64. eXALLOCAllocatorId_D3D,
  65. XALLOC_PHYSICAL_ALIGNMENT_4K,
  66. XALLOC_MEMPROTECT_WRITECOMBINE,
  67. FALSE,
  68. XALLOC_MEMTYPE_PHYSICAL );
  69. m_nTotalAllocations++;
  70. m_nTotalSize += AlignValue( bytes, 4096 );
  71. return XMemAlloc( bytes, attributes );
  72. }
  73. static void Free( void *p )
  74. {
  75. DWORD attributes = MAKE_XALLOC_ATTRIBUTES(
  76. 0,
  77. false,
  78. TRUE,
  79. FALSE,
  80. eXALLOCAllocatorId_D3D,
  81. XALLOC_PHYSICAL_ALIGNMENT_4K,
  82. XALLOC_MEMPROTECT_WRITECOMBINE,
  83. FALSE,
  84. XALLOC_MEMTYPE_PHYSICAL );
  85. m_nTotalAllocations--;
  86. m_nTotalSize -= XMemSize( p, attributes );
  87. XMemFree( p, attributes );
  88. }
  89. static int GetAllocations()
  90. {
  91. return m_nTotalAllocations;
  92. }
  93. static int GetSize()
  94. {
  95. return m_nTotalSize;
  96. }
  97. static int m_nTotalSize;
  98. static int m_nTotalAllocations;
  99. };
  100. int CD3DTextureAllocator::m_nTotalSize;
  101. int CD3DTextureAllocator::m_nTotalAllocations;
  102. enum TextureAllocator_t
  103. {
  104. TA_DEFAULT,
  105. TA_MIXED,
  106. TA_UNKNOWN,
  107. };
  108. struct THBaseInfo
  109. {
  110. TextureAllocator_t m_fAllocator;
  111. int m_TextureSize; // stored for delayed allocations
  112. };
  113. struct THInfo_t : public THBaseInfo
  114. {
  115. // Mixed heap info
  116. int nLogicalBytes;
  117. int nBytes;
  118. bool bFree:1;
  119. bool bNonTexture:1;
  120. THInfo_t *pPrev, *pNext;
  121. };
  122. struct THFreeBlock_t
  123. {
  124. THInfo_t heapInfo;
  125. THFreeBlock_t *pPrevFree, *pNextFree;
  126. };
  127. class CXboxTexture : public IDirect3DTexture, public THInfo_t
  128. {
  129. public:
  130. CXboxTexture()
  131. : bImmobile(false)
  132. {
  133. }
  134. bool bImmobile;
  135. bool CanRelocate() { return ( !bImmobile && !IsBusy() ); }
  136. };
  137. class CXboxCubeTexture : public IDirect3DCubeTexture, public THBaseInfo
  138. {
  139. };
  140. class CXboxVolumeTexture : public IDirect3DVolumeTexture, public THBaseInfo
  141. {
  142. };
  143. void SetD3DTextureImmobile( IDirect3DBaseTexture *pTexture, bool bImmobile )
  144. {
  145. if ( pTexture->GetType() == D3DRTYPE_TEXTURE )
  146. {
  147. (( CXboxTexture *)pTexture)->bImmobile = bImmobile;
  148. }
  149. }
  150. CXboxTexture *GetTexture( THInfo_t *pInfo )
  151. {
  152. if ( !pInfo->bFree && !pInfo->bNonTexture )
  153. {
  154. return (CXboxTexture *)((byte *)pInfo - offsetof( CXboxTexture, m_fAllocator ));
  155. }
  156. return NULL;
  157. }
  158. inline THFreeBlock_t *GetFreeBlock( THInfo_t *pInfo )
  159. {
  160. if ( pInfo->bFree )
  161. {
  162. return (THFreeBlock_t *)((byte *)pInfo - offsetof( THFreeBlock_t, heapInfo ));
  163. }
  164. return NULL;
  165. }
  166. class CMixedTextureHeap
  167. {
  168. enum
  169. {
  170. SIZE_ALIGNMENT = XBOX_HDD_SECTORSIZE,
  171. MIN_BLOCK_SIZE = 1024,
  172. };
  173. public:
  174. CMixedTextureHeap() :
  175. m_nLogicalBytes( 0 ),
  176. m_nActualBytes( 0 ),
  177. m_nAllocs( 0 ),
  178. m_nOldBytes( 0 ),
  179. m_nNonTextureAllocs( 0 ),
  180. m_nBytesTotal( 0 ),
  181. m_pBase( NULL ),
  182. m_pFirstFree( NULL )
  183. {
  184. }
  185. void Init()
  186. {
  187. extern ConVar mat_texturecachesize;
  188. MEM_ALLOC_CREDIT_("CMixedTextureHeap");
  189. m_nBytesTotal = ( mat_texturecachesize.GetInt() * 1024 * 1024 );
  190. #if 0
  191. m_nBytesTotal = AlignValue( m_nBytesTotal, SIZE_ALIGNMENT );
  192. m_pBase = CD3DTextureAllocator::Alloc( m_nBytesTotal );
  193. #else
  194. m_nBytesTotal = AlignValue( m_nBytesTotal, 16*1024*1024 );
  195. m_pBase = XPhysicalAlloc( m_nBytesTotal, MAXULONG_PTR, 4096, PAGE_READWRITE | PAGE_WRITECOMBINE | MEM_16MB_PAGES );
  196. #endif
  197. m_pFirstFree = (THFreeBlock_t *)m_pBase;
  198. m_pFirstFree->heapInfo.bFree = true;
  199. m_pFirstFree->heapInfo.bNonTexture = false;
  200. m_pFirstFree->heapInfo.nBytes = m_nBytesTotal;
  201. m_pFirstFree->heapInfo.pNext = NULL;
  202. m_pFirstFree->heapInfo.pPrev = NULL;
  203. m_pFirstFree->pNextFree = NULL;
  204. m_pFirstFree->pPrevFree = NULL;
  205. m_pLastFree = m_pFirstFree;
  206. }
  207. void *Alloc( int bytes, THInfo_t *pInfo, bool bNonTexture = false )
  208. {
  209. pInfo->nBytes = AlignValue( bytes, SIZE_ALIGNMENT );
  210. if ( !m_pBase )
  211. {
  212. Init();
  213. }
  214. if ( bNonTexture && m_nNonTextureAllocs == 0 )
  215. {
  216. Compact();
  217. }
  218. void *p = FindBlock( pInfo );
  219. if ( !p )
  220. {
  221. p = ExpandToFindBlock( pInfo );
  222. }
  223. if ( p )
  224. {
  225. pInfo->nLogicalBytes = bytes;
  226. pInfo->bNonTexture = bNonTexture;
  227. m_nLogicalBytes += bytes;
  228. if ( !IsRetail() )
  229. {
  230. m_nOldBytes += AlignValue( bytes, 4096 );
  231. }
  232. m_nActualBytes += pInfo->nBytes;
  233. m_nAllocs++;
  234. if ( bNonTexture )
  235. {
  236. m_nNonTextureAllocs++;
  237. }
  238. }
  239. return p;
  240. }
  241. void Free( void *p, THInfo_t *pInfo )
  242. {
  243. if ( !p )
  244. {
  245. return;
  246. }
  247. if ( !IsRetail() )
  248. {
  249. m_nOldBytes -= AlignValue( pInfo->nLogicalBytes, 4096 );
  250. }
  251. if ( pInfo->bNonTexture )
  252. {
  253. m_nNonTextureAllocs--;
  254. }
  255. m_nLogicalBytes -= pInfo->nLogicalBytes;
  256. m_nAllocs--;
  257. m_nActualBytes -= pInfo->nBytes;
  258. THFreeBlock_t *pFree = (THFreeBlock_t *)p;
  259. pFree->heapInfo = *pInfo;
  260. pFree->heapInfo.bFree = true;
  261. AddToBlocksList( &pFree->heapInfo, pFree->heapInfo.pPrev, pFree->heapInfo.pNext );
  262. pFree = MergeLeft( pFree );
  263. pFree = MergeRight( pFree );
  264. AddToFreeList( pFree );
  265. if ( pInfo->bNonTexture && m_nNonTextureAllocs == 0 )
  266. {
  267. Compact();
  268. }
  269. }
  270. int Size( void *p, THInfo_t *pInfo )
  271. {
  272. return AlignValue( pInfo->nBytes, SIZE_ALIGNMENT );
  273. }
  274. bool IsOwner( void *p )
  275. {
  276. return ( m_pBase && p >= m_pBase && p < (byte *)m_pBase + m_nBytesTotal );
  277. }
  278. //-----------------------------------------------------
  279. void *FindBlock( THInfo_t *pInfo )
  280. {
  281. THFreeBlock_t *pCurrent = m_pFirstFree;
  282. int nBytesDesired = pInfo->nBytes;
  283. // Find the first block big enough to hold, then split it if appropriate
  284. while ( pCurrent && pCurrent->heapInfo.nBytes < nBytesDesired )
  285. {
  286. pCurrent = pCurrent->pNextFree;
  287. }
  288. if ( pCurrent )
  289. {
  290. return ClaimBlock( pCurrent, pInfo );
  291. }
  292. return NULL;
  293. }
  294. void AddToFreeList( THFreeBlock_t *pFreeBlock )
  295. {
  296. if ( !IsRetail() )
  297. {
  298. pFreeBlock->heapInfo.nLogicalBytes = 0;
  299. }
  300. if ( m_pFirstFree )
  301. {
  302. THFreeBlock_t *pPrev = NULL;
  303. THFreeBlock_t *pNext = m_pFirstFree;
  304. int nBytes = pFreeBlock->heapInfo.nBytes;
  305. while ( pNext && pNext->heapInfo.nBytes < nBytes )
  306. {
  307. pPrev = pNext;
  308. pNext = pNext->pNextFree;
  309. }
  310. pFreeBlock->pPrevFree = pPrev;
  311. pFreeBlock->pNextFree = pNext;
  312. if ( pPrev )
  313. {
  314. pPrev->pNextFree = pFreeBlock;
  315. }
  316. else
  317. {
  318. m_pFirstFree = pFreeBlock;
  319. }
  320. if ( pNext )
  321. {
  322. pNext->pPrevFree = pFreeBlock;
  323. }
  324. else
  325. {
  326. m_pLastFree = pFreeBlock;
  327. }
  328. }
  329. else
  330. {
  331. pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL;
  332. m_pLastFree = m_pFirstFree = pFreeBlock;
  333. }
  334. }
  335. void RemoveFromFreeList( THFreeBlock_t *pFreeBlock )
  336. {
  337. if ( m_pFirstFree == pFreeBlock )
  338. {
  339. m_pFirstFree = m_pFirstFree->pNextFree;
  340. }
  341. else if ( pFreeBlock->pPrevFree )
  342. {
  343. pFreeBlock->pPrevFree->pNextFree = pFreeBlock->pNextFree;
  344. }
  345. if ( m_pLastFree == pFreeBlock )
  346. {
  347. m_pLastFree = pFreeBlock->pPrevFree;
  348. }
  349. else if ( pFreeBlock->pNextFree )
  350. {
  351. pFreeBlock->pNextFree->pPrevFree = pFreeBlock->pPrevFree;
  352. }
  353. pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL;
  354. }
  355. THFreeBlock_t *GetLastFree()
  356. {
  357. return m_pLastFree;
  358. }
  359. void AddToBlocksList( THInfo_t *pBlock, THInfo_t *pPrev, THInfo_t *pNext )
  360. {
  361. if ( pPrev )
  362. {
  363. pPrev->pNext = pBlock;
  364. }
  365. if ( pNext)
  366. {
  367. pNext->pPrev = pBlock;
  368. }
  369. pBlock->pPrev = pPrev;
  370. pBlock->pNext = pNext;
  371. }
  372. void RemoveFromBlocksList( THInfo_t *pBlock )
  373. {
  374. if ( pBlock->pPrev )
  375. {
  376. pBlock->pPrev->pNext = pBlock->pNext;
  377. }
  378. if ( pBlock->pNext )
  379. {
  380. pBlock->pNext->pPrev = pBlock->pPrev;
  381. }
  382. }
  383. //-----------------------------------------------------
  384. void *ClaimBlock( THFreeBlock_t *pFreeBlock, THInfo_t *pInfo )
  385. {
  386. RemoveFromFreeList( pFreeBlock );
  387. int nBytesDesired = pInfo->nBytes;
  388. int nBytesRemainder = pFreeBlock->heapInfo.nBytes - nBytesDesired;
  389. *pInfo = pFreeBlock->heapInfo;
  390. pInfo->bFree = false;
  391. pInfo->bNonTexture = false;
  392. if ( nBytesRemainder >= MIN_BLOCK_SIZE )
  393. {
  394. pInfo->nBytes = nBytesDesired;
  395. THFreeBlock_t *pRemainder = (THFreeBlock_t *)(((byte *)(pFreeBlock)) + nBytesDesired);
  396. pRemainder->heapInfo.bFree = true;
  397. pRemainder->heapInfo.nBytes = nBytesRemainder;
  398. AddToBlocksList( &pRemainder->heapInfo, pInfo, pInfo->pNext );
  399. AddToFreeList( pRemainder );
  400. }
  401. AddToBlocksList( pInfo, pInfo->pPrev, pInfo->pNext );
  402. return pFreeBlock;
  403. }
  404. THFreeBlock_t *MergeLeft( THFreeBlock_t *pFree )
  405. {
  406. THInfo_t *pPrev = pFree->heapInfo.pPrev;
  407. if ( pPrev && pPrev->bFree )
  408. {
  409. pPrev->nBytes += pFree->heapInfo.nBytes;
  410. RemoveFromBlocksList( &pFree->heapInfo );
  411. pFree = GetFreeBlock( pPrev );
  412. RemoveFromFreeList( pFree );
  413. }
  414. return pFree;
  415. }
  416. THFreeBlock_t *MergeRight( THFreeBlock_t *pFree )
  417. {
  418. THInfo_t *pNext = pFree->heapInfo.pNext;
  419. if ( pNext && pNext->bFree )
  420. {
  421. pFree->heapInfo.nBytes += pNext->nBytes;
  422. RemoveFromBlocksList( pNext );
  423. RemoveFromFreeList( GetFreeBlock( pNext ) );
  424. }
  425. return pFree;
  426. }
  427. //-----------------------------------------------------
  428. bool GetExpansionList( THFreeBlock_t *pFreeBlock, THInfo_t **ppStart, THInfo_t **ppEnd, int depth = 1 )
  429. {
  430. THInfo_t *pStart;
  431. THInfo_t *pEnd;
  432. int i;
  433. pStart = &pFreeBlock->heapInfo;
  434. pEnd = &pFreeBlock->heapInfo;
  435. if ( m_nNonTextureAllocs > 0 )
  436. {
  437. return false;
  438. }
  439. // Walk backwards to start of expansion
  440. i = depth;
  441. while ( i > 0 && pStart->pPrev)
  442. {
  443. THInfo_t *pScan = pStart->pPrev;
  444. while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() )
  445. {
  446. pScan = pScan->pPrev;
  447. i--;
  448. }
  449. if ( !pScan || !pScan->bFree )
  450. {
  451. break;
  452. }
  453. pStart = pScan;
  454. }
  455. // Walk forwards to start of expansion
  456. i = depth;
  457. while ( i > 0 && pEnd->pNext)
  458. {
  459. THInfo_t *pScan = pStart->pNext;
  460. while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() )
  461. {
  462. pScan = pScan->pNext;
  463. i--;
  464. }
  465. if ( !pScan || !pScan->bFree )
  466. {
  467. break;
  468. }
  469. pEnd = pScan;
  470. }
  471. *ppStart = pStart;
  472. *ppEnd = pEnd;
  473. return ( pStart != pEnd );
  474. }
  475. THFreeBlock_t *CompactExpansionList( THInfo_t *pStart, THInfo_t *pEnd )
  476. {
  477. // X360TBD:
  478. Assert( 0 );
  479. return NULL;
  480. #if 0
  481. #ifdef TH_PARANOID
  482. Validate();
  483. #endif
  484. StrongAssert( pStart->bFree );
  485. StrongAssert( pEnd->bFree );
  486. byte *pNextBlock = (byte *)pStart;
  487. THInfo_t *pTextureBlock = pStart;
  488. THInfo_t *pLastBlock = pStart->pPrev;
  489. while ( pTextureBlock != pEnd )
  490. {
  491. CXboxTexture *pTexture = GetTexture( pTextureBlock );
  492. // If it's a texture, move it and thread it on. Otherwise, discard it
  493. if ( pTexture )
  494. {
  495. void *pTextureBits = GetD3DTextureBasePtr( pTexture );
  496. int nBytes = pTextureBlock->nBytes;
  497. if ( pNextBlock + nBytes <= pTextureBits)
  498. {
  499. memcpy( pNextBlock, pTextureBits, nBytes );
  500. }
  501. else
  502. {
  503. memmove( pNextBlock, pTextureBits, nBytes );
  504. }
  505. pTexture->Data = 0;
  506. pTexture->Register( pNextBlock );
  507. pNextBlock += nBytes;
  508. if ( pLastBlock)
  509. {
  510. pLastBlock->pNext = pTextureBlock;
  511. }
  512. pTextureBlock->pPrev = pLastBlock;
  513. pLastBlock = pTextureBlock;
  514. }
  515. else
  516. {
  517. StrongAssert( pTextureBlock->bFree );
  518. RemoveFromFreeList( GetFreeBlock( pTextureBlock ) );
  519. }
  520. pTextureBlock = pTextureBlock->pNext;
  521. }
  522. RemoveFromFreeList( GetFreeBlock( pEnd ) );
  523. // Make a new block and fix up the block lists
  524. THFreeBlock_t *pFreeBlock = (THFreeBlock_t *)pNextBlock;
  525. pFreeBlock->heapInfo.pPrev = pLastBlock;
  526. pLastBlock->pNext = &pFreeBlock->heapInfo;
  527. pFreeBlock->heapInfo.pNext = pEnd->pNext;
  528. if ( pEnd->pNext )
  529. {
  530. pEnd->pNext->pPrev = &pFreeBlock->heapInfo;
  531. }
  532. pFreeBlock->heapInfo.bFree = true;
  533. pFreeBlock->heapInfo.nBytes = ( (byte *)pEnd - pNextBlock ) + pEnd->nBytes;
  534. AddToFreeList( pFreeBlock );
  535. #ifdef TH_PARANOID
  536. Validate();
  537. #endif
  538. return pFreeBlock;
  539. #endif
  540. }
  541. THFreeBlock_t *ExpandBlock( THFreeBlock_t *pFreeBlock, int depth = 1 )
  542. {
  543. THInfo_t *pStart;
  544. THInfo_t *pEnd;
  545. if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, depth ) )
  546. {
  547. return CompactExpansionList( pStart, pEnd );
  548. }
  549. return pFreeBlock;
  550. }
  551. THFreeBlock_t *ExpandBlockToFit( THFreeBlock_t *pFreeBlock, unsigned bytes )
  552. {
  553. if ( pFreeBlock )
  554. {
  555. THInfo_t *pStart;
  556. THInfo_t *pEnd;
  557. if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, 2 ) )
  558. {
  559. unsigned sum = 0;
  560. THInfo_t *pCurrent = pStart;
  561. while( pCurrent != pEnd->pNext )
  562. {
  563. if ( pCurrent->bFree )
  564. {
  565. sum += pCurrent->nBytes;
  566. }
  567. pCurrent = pCurrent->pNext;
  568. }
  569. if ( sum >= bytes )
  570. {
  571. pFreeBlock = CompactExpansionList( pStart, pEnd );
  572. }
  573. }
  574. }
  575. return pFreeBlock;
  576. }
  577. void *ExpandToFindBlock( THInfo_t *pInfo )
  578. {
  579. THFreeBlock_t *pFreeBlock = ExpandBlockToFit( GetLastFree(), pInfo->nBytes );
  580. if ( pFreeBlock && pFreeBlock->heapInfo.nBytes >= pInfo->nBytes )
  581. {
  582. return ClaimBlock( pFreeBlock, pInfo );
  583. }
  584. return NULL;
  585. }
  586. void Compact()
  587. {
  588. if ( m_nNonTextureAllocs > 0 )
  589. {
  590. return;
  591. }
  592. for (;;)
  593. {
  594. THFreeBlock_t *pCurrent = m_pFirstFree;
  595. THFreeBlock_t *pNew;
  596. while ( pCurrent )
  597. {
  598. int nBytesOld = pCurrent->heapInfo.nBytes;
  599. pNew = ExpandBlock( pCurrent, 999999 );
  600. if ( pNew != pCurrent || pNew->heapInfo.nBytes != nBytesOld )
  601. {
  602. #ifdef TH_PARANOID
  603. Validate();
  604. #endif
  605. break;
  606. }
  607. #ifdef TH_PARANOID
  608. pNew = ExpandBlock( pCurrent, 999999 );
  609. StrongAssert( pNew == pCurrent && pNew->heapInfo.nBytes == nBytesOld );
  610. #endif
  611. pCurrent = pCurrent->pNextFree;
  612. }
  613. if ( !pCurrent )
  614. {
  615. break;
  616. }
  617. }
  618. }
  619. void Validate()
  620. {
  621. if ( !m_pFirstFree )
  622. {
  623. return;
  624. }
  625. if ( m_nNonTextureAllocs > 0 )
  626. {
  627. return;
  628. }
  629. THInfo_t *pLast = NULL;
  630. THInfo_t *pInfo = &m_pFirstFree->heapInfo;
  631. while ( pInfo->pPrev )
  632. {
  633. pInfo = pInfo->pPrev;
  634. }
  635. void *pNextExpectedAddress = m_pBase;
  636. while ( pInfo )
  637. {
  638. byte *pCurrentAddress = (byte *)(( pInfo->bFree ) ? GetFreeBlock( pInfo ) : GetD3DTextureBasePtr( GetTexture( pInfo ) ) );
  639. StrongAssert( pCurrentAddress == pNextExpectedAddress );
  640. StrongAssert( pInfo->pPrev == pLast );
  641. pNextExpectedAddress = pCurrentAddress + pInfo->nBytes;
  642. pLast = pInfo;
  643. pInfo = pInfo->pNext;
  644. }
  645. THFreeBlock_t *pFree = m_pFirstFree;
  646. THFreeBlock_t *pLastFree = NULL;
  647. int nBytesHeap = XPhysicalSize( m_pBase );
  648. while ( pFree )
  649. {
  650. StrongAssert( pFree->pPrevFree == pLastFree );
  651. StrongAssert( (void *)pFree >= m_pBase && (void *)pFree < (byte *)m_pBase + nBytesHeap );
  652. StrongAssert( !pFree->pPrevFree || ( (void *)pFree->pPrevFree >= m_pBase && (void *)pFree->pPrevFree < (byte *)m_pBase + nBytesHeap ) );
  653. StrongAssert( !pFree->pNextFree || ( (void *)pFree->pNextFree >= m_pBase && (void *)pFree->pNextFree < (byte *)m_pBase + nBytesHeap ) );
  654. StrongAssert( !pFree->pPrevFree || pFree->pPrevFree->heapInfo.nBytes <= pFree->heapInfo.nBytes );
  655. pLastFree = pFree;
  656. pFree = pFree->pNextFree;
  657. }
  658. }
  659. //-----------------------------------------------------
  660. THFreeBlock_t *m_pFirstFree;
  661. THFreeBlock_t *m_pLastFree;
  662. void *m_pBase;
  663. int m_nLogicalBytes;
  664. int m_nActualBytes;
  665. int m_nAllocs;
  666. int m_nOldBytes;
  667. int m_nNonTextureAllocs;
  668. int m_nBytesTotal;
  669. };
  670. //-----------------------------------------------------------------------------
  671. inline TextureAllocator_t GetTextureAllocator( IDirect3DBaseTexture9 *pTexture )
  672. {
  673. return ( pTexture->GetType() == D3DRTYPE_CUBETEXTURE ) ? (( CXboxCubeTexture *)pTexture)->m_fAllocator : (( CXboxTexture *)pTexture)->m_fAllocator;
  674. }
  675. //-----------------------------------------------------------------------------
  676. CMixedTextureHeap g_MixedTextureHeap;
  677. CON_COMMAND( mat_texture_heap_stats, "" )
  678. {
  679. if ( UseStandardAllocator() )
  680. {
  681. Msg( "Texture heap stats: (Standard Allocator)\n" );
  682. Msg( "Allocations:%d Size:%d\n", CD3DTextureAllocator::GetAllocations(), CD3DTextureAllocator::GetSize() );
  683. }
  684. else
  685. {
  686. Msg( "Texture heap stats:\n" );
  687. Msg( " Mixed textures: %dk/%dk allocated in %d textures\n", g_MixedTextureHeap.m_nLogicalBytes/1024, g_MixedTextureHeap.m_nActualBytes/1024, g_MixedTextureHeap.m_nAllocs );
  688. float oldFootprint = g_MixedTextureHeap.m_nOldBytes;
  689. float newFootprint = g_MixedTextureHeap.m_nActualBytes;
  690. Msg( "\n Old: %.3fmb, New: %.3fmb\n", oldFootprint / (1024.0*1024.0), newFootprint / (1024.0*1024.0) );
  691. }
  692. }
  693. CON_COMMAND( mat_texture_heap_compact, "" )
  694. {
  695. Msg( "Validating texture heap...\n" );
  696. g_MixedTextureHeap.Validate();
  697. Msg( "Compacting texture heap...\n" );
  698. unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
  699. g_MixedTextureHeap.Compact();
  700. unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
  701. Msg( "\n Old largest block: %.3fk, New largest block: %.3fk\n\n", oldLargest / 1024.0, newLargest / 1024.0 );
  702. Msg( "Validating texture heap...\n" );
  703. g_MixedTextureHeap.Validate();
  704. Msg( "Done.\n" );
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Nasty back doors
  708. //-----------------------------------------------------------------------------
  709. void CompactTextureHeap()
  710. {
  711. unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
  712. g_MixedTextureHeap.Compact();
  713. unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
  714. DevMsg( "Compacted texture heap. Old largest block: %.3fk, New largest block: %.3fk\n", oldLargest / 1024.0, newLargest / 1024.0 );
  715. }
  716. CTextureHeap g_TextureHeap;
  717. //-----------------------------------------------------------------------------
  718. // Build and alloc a texture resource
  719. //-----------------------------------------------------------------------------
  720. IDirect3DTexture *CTextureHeap::AllocTexture( int width, int height, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory )
  721. {
  722. CXboxTexture* pD3DTexture = new CXboxTexture;
  723. // create a texture with contiguous mips and packed tails
  724. DWORD dwTextureSize = XGSetTextureHeaderEx(
  725. width,
  726. height,
  727. levels,
  728. usage,
  729. d3dFormat,
  730. 0,
  731. 0,
  732. 0,
  733. XGHEADER_CONTIGUOUS_MIP_OFFSET,
  734. 0,
  735. pD3DTexture,
  736. NULL,
  737. NULL );
  738. // based on "Xbox 360 Texture Storage"
  739. // can truncate the terminal tile using packed tails
  740. // the terminal tile must be at 32x32 or 16x16 packed
  741. if ( width == height && levels != 0 )
  742. {
  743. int terminalWidth = width >> (levels - 1);
  744. if ( d3dFormat == D3DFMT_DXT1 )
  745. {
  746. if ( terminalWidth <= 32 )
  747. {
  748. dwTextureSize -= 4*1024;
  749. }
  750. }
  751. else if ( d3dFormat == D3DFMT_DXT5 )
  752. {
  753. if ( terminalWidth == 32 )
  754. {
  755. dwTextureSize -= 8*1024;
  756. }
  757. else if ( terminalWidth <= 16 )
  758. {
  759. dwTextureSize -= 12*1024;
  760. }
  761. }
  762. }
  763. pD3DTexture->m_TextureSize = dwTextureSize;
  764. if ( !bFallback && bNoD3DMemory )
  765. {
  766. pD3DTexture->m_fAllocator = TA_UNKNOWN;
  767. return pD3DTexture;
  768. }
  769. void *pBuffer;
  770. if ( UseStandardAllocator() )
  771. {
  772. MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" );
  773. pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize );
  774. pD3DTexture->m_fAllocator = TA_DEFAULT;
  775. }
  776. else
  777. {
  778. MEM_ALLOC_CREDIT_( __FILE__ ": Mixed texture" );
  779. pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture );
  780. if ( pBuffer )
  781. {
  782. pD3DTexture->m_fAllocator = TA_MIXED;
  783. }
  784. else
  785. {
  786. g_MixedTextureHeap.Compact();
  787. pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture );
  788. if ( pBuffer )
  789. {
  790. pD3DTexture->m_fAllocator = TA_MIXED;
  791. }
  792. else
  793. {
  794. pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize );
  795. pD3DTexture->m_fAllocator = TA_DEFAULT;
  796. }
  797. }
  798. }
  799. if ( !pBuffer )
  800. {
  801. delete pD3DTexture;
  802. return NULL;
  803. }
  804. XGOffsetResourceAddress( pD3DTexture, pBuffer );
  805. return pD3DTexture;
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Build and alloc a cube texture resource
  809. //-----------------------------------------------------------------------------
  810. IDirect3DCubeTexture *CTextureHeap::AllocCubeTexture( int width, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory )
  811. {
  812. CXboxCubeTexture* pD3DCubeTexture = new CXboxCubeTexture;
  813. // create a cube texture with contiguous mips and packed tails
  814. DWORD dwTextureSize = XGSetCubeTextureHeaderEx(
  815. width,
  816. levels,
  817. usage,
  818. d3dFormat,
  819. 0,
  820. 0,
  821. 0,
  822. XGHEADER_CONTIGUOUS_MIP_OFFSET,
  823. pD3DCubeTexture,
  824. NULL,
  825. NULL );
  826. pD3DCubeTexture->m_TextureSize = dwTextureSize;
  827. if ( !bFallback && bNoD3DMemory )
  828. {
  829. pD3DCubeTexture->m_fAllocator = TA_UNKNOWN;
  830. return pD3DCubeTexture;
  831. }
  832. void *pBits;
  833. if ( UseStandardAllocator() )
  834. {
  835. MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" );
  836. pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
  837. pD3DCubeTexture->m_fAllocator = TA_DEFAULT;
  838. }
  839. else
  840. {
  841. // @todo: switch to texture heap
  842. MEM_ALLOC_CREDIT_( __FILE__ ": Odd sized cubemap textures" );
  843. // Really only happens with environment map
  844. pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
  845. pD3DCubeTexture->m_fAllocator = TA_DEFAULT;
  846. }
  847. if ( !pBits )
  848. {
  849. delete pD3DCubeTexture;
  850. return NULL;
  851. }
  852. XGOffsetResourceAddress( pD3DCubeTexture, pBits );
  853. return pD3DCubeTexture;
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Allocate an Volume Texture
  857. //-----------------------------------------------------------------------------
  858. IDirect3DVolumeTexture *CTextureHeap::AllocVolumeTexture( int width, int height, int depth, int levels, DWORD usage, D3DFORMAT d3dFormat )
  859. {
  860. CXboxVolumeTexture *pD3DVolumeTexture = new CXboxVolumeTexture;
  861. // create a cube texture with contiguous mips and packed tails
  862. DWORD dwTextureSize = XGSetVolumeTextureHeaderEx(
  863. width,
  864. height,
  865. depth,
  866. levels,
  867. usage,
  868. d3dFormat,
  869. 0,
  870. 0,
  871. 0,
  872. XGHEADER_CONTIGUOUS_MIP_OFFSET,
  873. pD3DVolumeTexture,
  874. NULL,
  875. NULL );
  876. void *pBits;
  877. MEM_ALLOC_CREDIT_( __FILE__ ": Volume standard D3D" );
  878. pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
  879. pD3DVolumeTexture->m_fAllocator = TA_DEFAULT;
  880. pD3DVolumeTexture->m_TextureSize = dwTextureSize;
  881. if ( !pBits )
  882. {
  883. delete pD3DVolumeTexture;
  884. return NULL;
  885. }
  886. XGOffsetResourceAddress( pD3DVolumeTexture, pBits );
  887. return pD3DVolumeTexture;
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Get current backbuffer multisample type (used in AllocRenderTargetSurface() )
  891. //-----------------------------------------------------------------------------
  892. D3DMULTISAMPLE_TYPE CTextureHeap::GetBackBufferMultiSampleType()
  893. {
  894. int backWidth, backHeight;
  895. ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
  896. // 2xMSAA at 640x480 and 848x480 are the only supported multisample mode on 360 (2xMSAA for 720p would
  897. // use predicated tiling, which would require a rewrite of *all* our render target code)
  898. // FIXME: shuffle the EDRAM surfaces to allow 4xMSAA for standard def
  899. // (they would overlap & trash each other with the current allocation scheme)
  900. D3DMULTISAMPLE_TYPE backBufferMultiSampleType = g_pShaderDevice->IsAAEnabled() ? D3DMULTISAMPLE_2_SAMPLES : D3DMULTISAMPLE_NONE;
  901. Assert( ( g_pShaderDevice->IsAAEnabled() == false ) || (backHeight == 480) );
  902. return backBufferMultiSampleType;
  903. }
  904. //-----------------------------------------------------------------------------
  905. // Allocate an EDRAM surface
  906. //-----------------------------------------------------------------------------
  907. IDirect3DSurface *CTextureHeap::AllocRenderTargetSurface( int width, int height, D3DFORMAT d3dFormat, bool bMultiSample, int base )
  908. {
  909. // render target surfaces don't need to exist simultaneously
  910. // force their allocations to overlap at the end of back buffer and zbuffer
  911. // this should leave 3MB (of 10) free assuming 1280x720 (and 5MB with 640x480@2xMSAA)
  912. D3DMULTISAMPLE_TYPE backBufferMultiSampleType = GetBackBufferMultiSampleType();
  913. D3DMULTISAMPLE_TYPE multiSampleType = bMultiSample ? backBufferMultiSampleType : D3DMULTISAMPLE_NONE;
  914. if ( base < 0 )
  915. {
  916. int backWidth, backHeight;
  917. ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
  918. D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() );
  919. base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, backBufferMultiSampleType );
  920. }
  921. D3DSURFACE_PARAMETERS surfParameters;
  922. surfParameters.Base = base;
  923. surfParameters.ColorExpBias = 0;
  924. if ( ( d3dFormat == D3DFMT_D24FS8 ) || ( d3dFormat == D3DFMT_D24S8 ) || ( d3dFormat == D3DFMT_D16 ) )
  925. {
  926. surfParameters.HierarchicalZBase = 0;
  927. if ( ( surfParameters.HierarchicalZBase + XGHierarchicalZSize( width, height, multiSampleType ) ) > GPU_HIERARCHICAL_Z_TILES )
  928. {
  929. // overflow, can't hold the tiles so disable
  930. surfParameters.HierarchicalZBase = 0xFFFFFFFF;
  931. }
  932. }
  933. else
  934. {
  935. // not using
  936. surfParameters.HierarchicalZBase = 0xFFFFFFFF;
  937. }
  938. HRESULT hr;
  939. IDirect3DSurface9 *pSurface = NULL;
  940. hr = Dx9Device()->CreateRenderTarget( width, height, d3dFormat, multiSampleType, 0, FALSE, &pSurface, &surfParameters );
  941. Assert( !FAILED( hr ) );
  942. return pSurface;
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Perform the real d3d allocation, returns true if succesful, false otherwise.
  946. // Only valid for a texture created with no d3d bits, otherwise no-op.
  947. //-----------------------------------------------------------------------------
  948. bool CTextureHeap::AllocD3DMemory( IDirect3DBaseTexture *pD3DTexture )
  949. {
  950. if ( !pD3DTexture )
  951. {
  952. return false;
  953. }
  954. if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
  955. {
  956. // there are no d3d bits for a surface
  957. return false;
  958. }
  959. void *pBits = GetD3DTextureBasePtr( pD3DTexture );
  960. if ( pBits )
  961. {
  962. // already have d3d bits
  963. return true;
  964. }
  965. if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
  966. {
  967. MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" );
  968. pBits = CD3DTextureAllocator::Alloc( ((CXboxTexture *)pD3DTexture)->m_TextureSize );
  969. ((CXboxTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT;
  970. XGOffsetResourceAddress( (CXboxTexture *)pD3DTexture, pBits );
  971. return true;
  972. }
  973. else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
  974. {
  975. MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" );
  976. pBits = CD3DTextureAllocator::Alloc( ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize );
  977. ((CXboxCubeTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT;
  978. XGOffsetResourceAddress( (CXboxCubeTexture *)pD3DTexture, pBits );
  979. return true;
  980. }
  981. return false;
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Release the allocated store
  985. //-----------------------------------------------------------------------------
  986. void CTextureHeap::FreeTexture( IDirect3DBaseTexture *pD3DTexture )
  987. {
  988. if ( !pD3DTexture )
  989. {
  990. return;
  991. }
  992. if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
  993. {
  994. // texture heap doesn't own render target surfaces
  995. // allow callers to call through for less higher level detection
  996. int ref = ((IDirect3DSurface*)pD3DTexture)->Release();
  997. Assert( ref == 0 );
  998. ref = ref; // Quiet "unused variable" warning in release
  999. return;
  1000. }
  1001. else
  1002. {
  1003. byte *pBits = (byte *)GetD3DTextureBasePtr( pD3DTexture );
  1004. if ( pBits )
  1005. {
  1006. switch ( GetTextureAllocator( pD3DTexture ) )
  1007. {
  1008. case TA_DEFAULT:
  1009. CD3DTextureAllocator::Free( pBits );
  1010. break;
  1011. case TA_MIXED:
  1012. g_MixedTextureHeap.Free( pBits, ((CXboxTexture *)pD3DTexture) );
  1013. break;
  1014. }
  1015. }
  1016. }
  1017. if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
  1018. {
  1019. delete (CXboxTexture *)pD3DTexture;
  1020. }
  1021. else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE )
  1022. {
  1023. delete (CXboxVolumeTexture *)pD3DTexture;
  1024. }
  1025. else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
  1026. {
  1027. delete (CXboxCubeTexture *)pD3DTexture;
  1028. }
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Returns the allocated footprint
  1032. //-----------------------------------------------------------------------------
  1033. int CTextureHeap::GetSize( IDirect3DBaseTexture *pD3DTexture )
  1034. {
  1035. if( pD3DTexture == NULL )
  1036. return 0;
  1037. if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
  1038. {
  1039. D3DSURFACE_DESC surfaceDesc;
  1040. HRESULT hr = ((IDirect3DSurface*)pD3DTexture)->GetDesc( &surfaceDesc );
  1041. Assert( !FAILED( hr ) );
  1042. hr = hr; // Quiet "unused variable" warning in release
  1043. int size = ImageLoader::GetMemRequired(
  1044. surfaceDesc.Width,
  1045. surfaceDesc.Height,
  1046. 0,
  1047. ImageLoader::D3DFormatToImageFormat( surfaceDesc.Format ),
  1048. false );
  1049. return size;
  1050. }
  1051. else if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
  1052. {
  1053. return ((CXboxTexture *)pD3DTexture)->m_TextureSize;
  1054. }
  1055. else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
  1056. {
  1057. return ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize;
  1058. }
  1059. else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE )
  1060. {
  1061. return ((CXboxVolumeTexture *)pD3DTexture)->m_TextureSize;
  1062. }
  1063. return 0;
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. // Crunch the pools
  1067. //-----------------------------------------------------------------------------
  1068. void CTextureHeap::Compact()
  1069. {
  1070. g_MixedTextureHeap.Compact();
  1071. }