summaryrefslogtreecommitdiff
path: root/materialsystem/shaderapidx9/textureheap.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/shaderapidx9/textureheap.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'materialsystem/shaderapidx9/textureheap.cpp')
-rw-r--r--materialsystem/shaderapidx9/textureheap.cpp1258
1 files changed, 1258 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/textureheap.cpp b/materialsystem/shaderapidx9/textureheap.cpp
new file mode 100644
index 0000000..b2c9a66
--- /dev/null
+++ b/materialsystem/shaderapidx9/textureheap.cpp
@@ -0,0 +1,1258 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "tier1/mempool.h"
+#include "tier1/convar.h"
+#include "tier1/utlmap.h"
+#include "shaderapidx8.h"
+#include "texturedx8.h"
+#include "textureheap.h"
+#include "shaderapidx8_global.h"
+
+#include "tier0/memdbgon.h"
+
+#define USE_STANDARD_ALLOCATOR
+#ifdef USE_STANDARD_ALLOCATOR
+#define UseStandardAllocator() (true)
+#elif !defined(_RETAIL)
+bool g_bUseStandardAllocator = false;
+bool UseStandardAllocator()
+{
+ static bool bReadCommandLine;
+ if ( !bReadCommandLine )
+ {
+ bReadCommandLine = true;
+ const char *pStr = Plat_GetCommandLine();
+ if ( pStr )
+ {
+ char tempStr[512];
+ Q_strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
+ tempStr[ sizeof( tempStr ) - 1 ] = 0;
+ _strlwr( tempStr );
+
+ if ( strstr( tempStr, "-notextureheap" ) )
+ g_bUseStandardAllocator = true;
+ }
+ }
+ return g_bUseStandardAllocator;
+}
+#else
+#define UseStandardAllocator() (false)
+#endif
+
+#if !defined( _RELEASE ) && !defined( _RETAIL )
+#define StrongAssert( expr ) if ( (expr) ) ; else { DebuggerBreak(); }
+#else
+#define StrongAssert( expr ) ((void)0)
+#endif
+
+//-----------------------------------------------------------------------------
+// Get Texture HW base
+//-----------------------------------------------------------------------------
+void *GetD3DTextureBasePtr( IDirect3DBaseTexture* pTex )
+{
+ // assumes base and mips are contiguous
+ return (void *)( (unsigned int)pTex->Format.BaseAddress << 12 );
+}
+
+class CD3DTextureAllocator
+{
+public:
+ static void *Alloc( int bytes )
+ {
+ DWORD attributes = MAKE_XALLOC_ATTRIBUTES(
+ 0,
+ false,
+ TRUE,
+ FALSE,
+ eXALLOCAllocatorId_D3D,
+ XALLOC_PHYSICAL_ALIGNMENT_4K,
+ XALLOC_MEMPROTECT_WRITECOMBINE,
+ FALSE,
+ XALLOC_MEMTYPE_PHYSICAL );
+ m_nTotalAllocations++;
+ m_nTotalSize += AlignValue( bytes, 4096 );
+ return XMemAlloc( bytes, attributes );
+ }
+
+ static void Free( void *p )
+ {
+ DWORD attributes = MAKE_XALLOC_ATTRIBUTES(
+ 0,
+ false,
+ TRUE,
+ FALSE,
+ eXALLOCAllocatorId_D3D,
+ XALLOC_PHYSICAL_ALIGNMENT_4K,
+ XALLOC_MEMPROTECT_WRITECOMBINE,
+ FALSE,
+ XALLOC_MEMTYPE_PHYSICAL );
+ m_nTotalAllocations--;
+ m_nTotalSize -= XMemSize( p, attributes );
+ XMemFree( p, attributes );
+ }
+
+ static int GetAllocations()
+ {
+ return m_nTotalAllocations;
+ }
+
+ static int GetSize()
+ {
+ return m_nTotalSize;
+ }
+
+ static int m_nTotalSize;
+ static int m_nTotalAllocations;
+};
+
+int CD3DTextureAllocator::m_nTotalSize;
+int CD3DTextureAllocator::m_nTotalAllocations;
+
+enum TextureAllocator_t
+{
+ TA_DEFAULT,
+ TA_MIXED,
+ TA_UNKNOWN,
+};
+
+struct THBaseInfo
+{
+ TextureAllocator_t m_fAllocator;
+ int m_TextureSize; // stored for delayed allocations
+};
+
+struct THInfo_t : public THBaseInfo
+{
+ // Mixed heap info
+ int nLogicalBytes;
+ int nBytes;
+ bool bFree:1;
+ bool bNonTexture:1;
+
+ THInfo_t *pPrev, *pNext;
+};
+
+struct THFreeBlock_t
+{
+ THInfo_t heapInfo;
+ THFreeBlock_t *pPrevFree, *pNextFree;
+};
+
+class CXboxTexture : public IDirect3DTexture, public THInfo_t
+{
+public:
+ CXboxTexture()
+ : bImmobile(false)
+ {
+ }
+
+ bool bImmobile;
+ bool CanRelocate() { return ( !bImmobile && !IsBusy() ); }
+};
+
+class CXboxCubeTexture : public IDirect3DCubeTexture, public THBaseInfo
+{
+};
+
+class CXboxVolumeTexture : public IDirect3DVolumeTexture, public THBaseInfo
+{
+};
+
+
+void SetD3DTextureImmobile( IDirect3DBaseTexture *pTexture, bool bImmobile )
+{
+ if ( pTexture->GetType() == D3DRTYPE_TEXTURE )
+ {
+ (( CXboxTexture *)pTexture)->bImmobile = bImmobile;
+ }
+}
+
+CXboxTexture *GetTexture( THInfo_t *pInfo )
+{
+ if ( !pInfo->bFree && !pInfo->bNonTexture )
+ {
+ return (CXboxTexture *)((byte *)pInfo - offsetof( CXboxTexture, m_fAllocator ));
+ }
+ return NULL;
+}
+
+inline THFreeBlock_t *GetFreeBlock( THInfo_t *pInfo )
+{
+ if ( pInfo->bFree )
+ {
+ return (THFreeBlock_t *)((byte *)pInfo - offsetof( THFreeBlock_t, heapInfo ));
+ }
+ return NULL;
+}
+
+class CMixedTextureHeap
+{
+ enum
+ {
+ SIZE_ALIGNMENT = XBOX_HDD_SECTORSIZE,
+ MIN_BLOCK_SIZE = 1024,
+ };
+public:
+
+ CMixedTextureHeap() :
+ m_nLogicalBytes( 0 ),
+ m_nActualBytes( 0 ),
+ m_nAllocs( 0 ),
+ m_nOldBytes( 0 ),
+ m_nNonTextureAllocs( 0 ),
+ m_nBytesTotal( 0 ),
+ m_pBase( NULL ),
+ m_pFirstFree( NULL )
+ {
+ }
+
+ void Init()
+ {
+ extern ConVar mat_texturecachesize;
+ MEM_ALLOC_CREDIT_("CMixedTextureHeap");
+
+ m_nBytesTotal = ( mat_texturecachesize.GetInt() * 1024 * 1024 );
+#if 0
+ m_nBytesTotal = AlignValue( m_nBytesTotal, SIZE_ALIGNMENT );
+ m_pBase = CD3DTextureAllocator::Alloc( m_nBytesTotal );
+#else
+ m_nBytesTotal = AlignValue( m_nBytesTotal, 16*1024*1024 );
+ m_pBase = XPhysicalAlloc( m_nBytesTotal, MAXULONG_PTR, 4096, PAGE_READWRITE | PAGE_WRITECOMBINE | MEM_16MB_PAGES );
+#endif
+ m_pFirstFree = (THFreeBlock_t *)m_pBase;
+
+
+ m_pFirstFree->heapInfo.bFree = true;
+ m_pFirstFree->heapInfo.bNonTexture = false;
+ m_pFirstFree->heapInfo.nBytes = m_nBytesTotal;
+ m_pFirstFree->heapInfo.pNext = NULL;
+ m_pFirstFree->heapInfo.pPrev = NULL;
+ m_pFirstFree->pNextFree = NULL;
+ m_pFirstFree->pPrevFree = NULL;
+
+ m_pLastFree = m_pFirstFree;
+ }
+
+ void *Alloc( int bytes, THInfo_t *pInfo, bool bNonTexture = false )
+ {
+ pInfo->nBytes = AlignValue( bytes, SIZE_ALIGNMENT );
+
+ if ( !m_pBase )
+ {
+ Init();
+ }
+
+ if ( bNonTexture && m_nNonTextureAllocs == 0 )
+ {
+ Compact();
+ }
+
+ void *p = FindBlock( pInfo );
+
+ if ( !p )
+ {
+ p = ExpandToFindBlock( pInfo );
+ }
+
+ if ( p )
+ {
+ pInfo->nLogicalBytes = bytes;
+ pInfo->bNonTexture = bNonTexture;
+ m_nLogicalBytes += bytes;
+ if ( !IsRetail() )
+ {
+ m_nOldBytes += AlignValue( bytes, 4096 );
+ }
+ m_nActualBytes += pInfo->nBytes;
+ m_nAllocs++;
+
+ if ( bNonTexture )
+ {
+ m_nNonTextureAllocs++;
+ }
+ }
+ return p;
+ }
+
+ void Free( void *p, THInfo_t *pInfo )
+ {
+ if ( !p )
+ {
+ return;
+ }
+
+ if ( !IsRetail() )
+ {
+ m_nOldBytes -= AlignValue( pInfo->nLogicalBytes, 4096 );
+ }
+
+ if ( pInfo->bNonTexture )
+ {
+ m_nNonTextureAllocs--;
+ }
+
+ m_nLogicalBytes -= pInfo->nLogicalBytes;
+ m_nAllocs--;
+ m_nActualBytes -= pInfo->nBytes;
+
+ THFreeBlock_t *pFree = (THFreeBlock_t *)p;
+ pFree->heapInfo = *pInfo;
+ pFree->heapInfo.bFree = true;
+
+ AddToBlocksList( &pFree->heapInfo, pFree->heapInfo.pPrev, pFree->heapInfo.pNext );
+
+ pFree = MergeLeft( pFree );
+ pFree = MergeRight( pFree );
+
+ AddToFreeList( pFree );
+
+ if ( pInfo->bNonTexture && m_nNonTextureAllocs == 0 )
+ {
+ Compact();
+ }
+ }
+
+ int Size( void *p, THInfo_t *pInfo )
+ {
+ return AlignValue( pInfo->nBytes, SIZE_ALIGNMENT );
+ }
+
+ bool IsOwner( void *p )
+ {
+ return ( m_pBase && p >= m_pBase && p < (byte *)m_pBase + m_nBytesTotal );
+ }
+
+ //-----------------------------------------------------
+
+ void *FindBlock( THInfo_t *pInfo )
+ {
+ THFreeBlock_t *pCurrent = m_pFirstFree;
+
+ int nBytesDesired = pInfo->nBytes;
+
+ // Find the first block big enough to hold, then split it if appropriate
+ while ( pCurrent && pCurrent->heapInfo.nBytes < nBytesDesired )
+ {
+ pCurrent = pCurrent->pNextFree;
+ }
+
+ if ( pCurrent )
+ {
+ return ClaimBlock( pCurrent, pInfo );
+ }
+
+ return NULL;
+ }
+
+ void AddToFreeList( THFreeBlock_t *pFreeBlock )
+ {
+ if ( !IsRetail() )
+ {
+ pFreeBlock->heapInfo.nLogicalBytes = 0;
+ }
+
+ if ( m_pFirstFree )
+ {
+ THFreeBlock_t *pPrev = NULL;
+ THFreeBlock_t *pNext = m_pFirstFree;
+
+ int nBytes = pFreeBlock->heapInfo.nBytes;
+
+ while ( pNext && pNext->heapInfo.nBytes < nBytes )
+ {
+ pPrev = pNext;
+ pNext = pNext->pNextFree;
+ }
+
+ pFreeBlock->pPrevFree = pPrev;
+ pFreeBlock->pNextFree = pNext;
+
+ if ( pPrev )
+ {
+ pPrev->pNextFree = pFreeBlock;
+ }
+ else
+ {
+ m_pFirstFree = pFreeBlock;
+ }
+
+ if ( pNext )
+ {
+ pNext->pPrevFree = pFreeBlock;
+ }
+ else
+ {
+ m_pLastFree = pFreeBlock;
+ }
+ }
+ else
+ {
+ pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL;
+ m_pLastFree = m_pFirstFree = pFreeBlock;
+ }
+ }
+
+ void RemoveFromFreeList( THFreeBlock_t *pFreeBlock )
+ {
+ if ( m_pFirstFree == pFreeBlock )
+ {
+ m_pFirstFree = m_pFirstFree->pNextFree;
+ }
+ else if ( pFreeBlock->pPrevFree )
+ {
+ pFreeBlock->pPrevFree->pNextFree = pFreeBlock->pNextFree;
+ }
+
+ if ( m_pLastFree == pFreeBlock )
+ {
+ m_pLastFree = pFreeBlock->pPrevFree;
+ }
+ else if ( pFreeBlock->pNextFree )
+ {
+ pFreeBlock->pNextFree->pPrevFree = pFreeBlock->pPrevFree;
+ }
+
+ pFreeBlock->pPrevFree = pFreeBlock->pNextFree = NULL;
+ }
+
+ THFreeBlock_t *GetLastFree()
+ {
+ return m_pLastFree;
+ }
+
+ void AddToBlocksList( THInfo_t *pBlock, THInfo_t *pPrev, THInfo_t *pNext )
+ {
+ if ( pPrev )
+ {
+ pPrev->pNext = pBlock;
+ }
+
+ if ( pNext)
+ {
+ pNext->pPrev = pBlock;
+ }
+
+ pBlock->pPrev = pPrev;
+ pBlock->pNext = pNext;
+ }
+
+ void RemoveFromBlocksList( THInfo_t *pBlock )
+ {
+ if ( pBlock->pPrev )
+ {
+ pBlock->pPrev->pNext = pBlock->pNext;
+ }
+
+ if ( pBlock->pNext )
+ {
+ pBlock->pNext->pPrev = pBlock->pPrev;
+ }
+ }
+
+ //-----------------------------------------------------
+
+ void *ClaimBlock( THFreeBlock_t *pFreeBlock, THInfo_t *pInfo )
+ {
+ RemoveFromFreeList( pFreeBlock );
+
+ int nBytesDesired = pInfo->nBytes;
+ int nBytesRemainder = pFreeBlock->heapInfo.nBytes - nBytesDesired;
+ *pInfo = pFreeBlock->heapInfo;
+ pInfo->bFree = false;
+ pInfo->bNonTexture = false;
+ if ( nBytesRemainder >= MIN_BLOCK_SIZE )
+ {
+ pInfo->nBytes = nBytesDesired;
+
+ THFreeBlock_t *pRemainder = (THFreeBlock_t *)(((byte *)(pFreeBlock)) + nBytesDesired);
+ pRemainder->heapInfo.bFree = true;
+ pRemainder->heapInfo.nBytes = nBytesRemainder;
+
+ AddToBlocksList( &pRemainder->heapInfo, pInfo, pInfo->pNext );
+ AddToFreeList( pRemainder );
+ }
+ AddToBlocksList( pInfo, pInfo->pPrev, pInfo->pNext );
+ return pFreeBlock;
+ }
+
+ THFreeBlock_t *MergeLeft( THFreeBlock_t *pFree )
+ {
+ THInfo_t *pPrev = pFree->heapInfo.pPrev;
+ if ( pPrev && pPrev->bFree )
+ {
+ pPrev->nBytes += pFree->heapInfo.nBytes;
+ RemoveFromBlocksList( &pFree->heapInfo );
+ pFree = GetFreeBlock( pPrev );
+ RemoveFromFreeList( pFree );
+ }
+ return pFree;
+ }
+
+ THFreeBlock_t *MergeRight( THFreeBlock_t *pFree )
+ {
+ THInfo_t *pNext = pFree->heapInfo.pNext;
+ if ( pNext && pNext->bFree )
+ {
+ pFree->heapInfo.nBytes += pNext->nBytes;
+ RemoveFromBlocksList( pNext );
+ RemoveFromFreeList( GetFreeBlock( pNext ) );
+ }
+ return pFree;
+ }
+
+ //-----------------------------------------------------
+
+ bool GetExpansionList( THFreeBlock_t *pFreeBlock, THInfo_t **ppStart, THInfo_t **ppEnd, int depth = 1 )
+ {
+ THInfo_t *pStart;
+ THInfo_t *pEnd;
+ int i;
+
+ pStart = &pFreeBlock->heapInfo;
+ pEnd = &pFreeBlock->heapInfo;
+
+ if ( m_nNonTextureAllocs > 0 )
+ {
+ return false;
+ }
+
+ // Walk backwards to start of expansion
+ i = depth;
+ while ( i > 0 && pStart->pPrev)
+ {
+ THInfo_t *pScan = pStart->pPrev;
+
+ while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() )
+ {
+ pScan = pScan->pPrev;
+ i--;
+ }
+
+ if ( !pScan || !pScan->bFree )
+ {
+ break;
+ }
+
+ pStart = pScan;
+ }
+
+ // Walk forwards to start of expansion
+ i = depth;
+ while ( i > 0 && pEnd->pNext)
+ {
+ THInfo_t *pScan = pStart->pNext;
+
+ while ( i > 0 && pScan && !pScan->bFree && GetTexture( pScan )->CanRelocate() )
+ {
+ pScan = pScan->pNext;
+ i--;
+ }
+
+ if ( !pScan || !pScan->bFree )
+ {
+ break;
+ }
+
+ pEnd = pScan;
+ }
+
+ *ppStart = pStart;
+ *ppEnd = pEnd;
+
+ return ( pStart != pEnd );
+ }
+
+ THFreeBlock_t *CompactExpansionList( THInfo_t *pStart, THInfo_t *pEnd )
+ {
+// X360TBD:
+Assert( 0 );
+return NULL;
+#if 0
+#ifdef TH_PARANOID
+ Validate();
+#endif
+ StrongAssert( pStart->bFree );
+ StrongAssert( pEnd->bFree );
+ byte *pNextBlock = (byte *)pStart;
+
+ THInfo_t *pTextureBlock = pStart;
+ THInfo_t *pLastBlock = pStart->pPrev;
+
+ while ( pTextureBlock != pEnd )
+ {
+ CXboxTexture *pTexture = GetTexture( pTextureBlock );
+ // If it's a texture, move it and thread it on. Otherwise, discard it
+ if ( pTexture )
+ {
+ void *pTextureBits = GetD3DTextureBasePtr( pTexture );
+ int nBytes = pTextureBlock->nBytes;
+
+ if ( pNextBlock + nBytes <= pTextureBits)
+ {
+ memcpy( pNextBlock, pTextureBits, nBytes );
+ }
+ else
+ {
+ memmove( pNextBlock, pTextureBits, nBytes );
+ }
+
+ pTexture->Data = 0;
+ pTexture->Register( pNextBlock );
+
+ pNextBlock += nBytes;
+ if ( pLastBlock)
+ {
+ pLastBlock->pNext = pTextureBlock;
+ }
+ pTextureBlock->pPrev = pLastBlock;
+ pLastBlock = pTextureBlock;
+ }
+ else
+ {
+ StrongAssert( pTextureBlock->bFree );
+ RemoveFromFreeList( GetFreeBlock( pTextureBlock ) );
+ }
+ pTextureBlock = pTextureBlock->pNext;
+ }
+
+ RemoveFromFreeList( GetFreeBlock( pEnd ) );
+
+ // Make a new block and fix up the block lists
+ THFreeBlock_t *pFreeBlock = (THFreeBlock_t *)pNextBlock;
+ pFreeBlock->heapInfo.pPrev = pLastBlock;
+ pLastBlock->pNext = &pFreeBlock->heapInfo;
+ pFreeBlock->heapInfo.pNext = pEnd->pNext;
+ if ( pEnd->pNext )
+ {
+ pEnd->pNext->pPrev = &pFreeBlock->heapInfo;
+ }
+ pFreeBlock->heapInfo.bFree = true;
+ pFreeBlock->heapInfo.nBytes = ( (byte *)pEnd - pNextBlock ) + pEnd->nBytes;
+
+ AddToFreeList( pFreeBlock );
+
+#ifdef TH_PARANOID
+ Validate();
+#endif
+ return pFreeBlock;
+#endif
+ }
+
+ THFreeBlock_t *ExpandBlock( THFreeBlock_t *pFreeBlock, int depth = 1 )
+ {
+ THInfo_t *pStart;
+ THInfo_t *pEnd;
+
+ if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, depth ) )
+ {
+ return CompactExpansionList( pStart, pEnd );
+ }
+
+ return pFreeBlock;
+ }
+
+ THFreeBlock_t *ExpandBlockToFit( THFreeBlock_t *pFreeBlock, unsigned bytes )
+ {
+ if ( pFreeBlock )
+ {
+ THInfo_t *pStart;
+ THInfo_t *pEnd;
+
+ if ( GetExpansionList( pFreeBlock, &pStart, &pEnd, 2 ) )
+ {
+ unsigned sum = 0;
+ THInfo_t *pCurrent = pStart;
+ while( pCurrent != pEnd->pNext )
+ {
+ if ( pCurrent->bFree )
+ {
+ sum += pCurrent->nBytes;
+ }
+ pCurrent = pCurrent->pNext;
+ }
+
+ if ( sum >= bytes )
+ {
+ pFreeBlock = CompactExpansionList( pStart, pEnd );
+ }
+ }
+ }
+
+ return pFreeBlock;
+ }
+
+ void *ExpandToFindBlock( THInfo_t *pInfo )
+ {
+ THFreeBlock_t *pFreeBlock = ExpandBlockToFit( GetLastFree(), pInfo->nBytes );
+ if ( pFreeBlock && pFreeBlock->heapInfo.nBytes >= pInfo->nBytes )
+ {
+ return ClaimBlock( pFreeBlock, pInfo );
+ }
+ return NULL;
+ }
+
+ void Compact()
+ {
+ if ( m_nNonTextureAllocs > 0 )
+ {
+ return;
+ }
+
+ for (;;)
+ {
+ THFreeBlock_t *pCurrent = m_pFirstFree;
+ THFreeBlock_t *pNew;
+ while ( pCurrent )
+ {
+ int nBytesOld = pCurrent->heapInfo.nBytes;
+ pNew = ExpandBlock( pCurrent, 999999 );
+
+ if ( pNew != pCurrent || pNew->heapInfo.nBytes != nBytesOld )
+ {
+#ifdef TH_PARANOID
+ Validate();
+#endif
+ break;
+ }
+
+#ifdef TH_PARANOID
+ pNew = ExpandBlock( pCurrent, 999999 );
+ StrongAssert( pNew == pCurrent && pNew->heapInfo.nBytes == nBytesOld );
+#endif
+
+ pCurrent = pCurrent->pNextFree;
+ }
+
+ if ( !pCurrent )
+ {
+ break;
+ }
+ }
+ }
+
+ void Validate()
+ {
+ if ( !m_pFirstFree )
+ {
+ return;
+ }
+
+ if ( m_nNonTextureAllocs > 0 )
+ {
+ return;
+ }
+
+ THInfo_t *pLast = NULL;
+ THInfo_t *pInfo = &m_pFirstFree->heapInfo;
+
+ while ( pInfo->pPrev )
+ {
+ pInfo = pInfo->pPrev;
+ }
+
+ void *pNextExpectedAddress = m_pBase;
+
+ while ( pInfo )
+ {
+ byte *pCurrentAddress = (byte *)(( pInfo->bFree ) ? GetFreeBlock( pInfo ) : GetD3DTextureBasePtr( GetTexture( pInfo ) ) );
+ StrongAssert( pCurrentAddress == pNextExpectedAddress );
+ StrongAssert( pInfo->pPrev == pLast );
+ pNextExpectedAddress = pCurrentAddress + pInfo->nBytes;
+ pLast = pInfo;
+ pInfo = pInfo->pNext;
+ }
+
+ THFreeBlock_t *pFree = m_pFirstFree;
+ THFreeBlock_t *pLastFree = NULL;
+ int nBytesHeap = XPhysicalSize( m_pBase );
+
+ while ( pFree )
+ {
+ StrongAssert( pFree->pPrevFree == pLastFree );
+ StrongAssert( (void *)pFree >= m_pBase && (void *)pFree < (byte *)m_pBase + nBytesHeap );
+ StrongAssert( !pFree->pPrevFree || ( (void *)pFree->pPrevFree >= m_pBase && (void *)pFree->pPrevFree < (byte *)m_pBase + nBytesHeap ) );
+ StrongAssert( !pFree->pNextFree || ( (void *)pFree->pNextFree >= m_pBase && (void *)pFree->pNextFree < (byte *)m_pBase + nBytesHeap ) );
+ StrongAssert( !pFree->pPrevFree || pFree->pPrevFree->heapInfo.nBytes <= pFree->heapInfo.nBytes );
+ pLastFree = pFree;
+ pFree = pFree->pNextFree;
+ }
+ }
+
+ //-----------------------------------------------------
+
+ THFreeBlock_t *m_pFirstFree;
+ THFreeBlock_t *m_pLastFree;
+ void *m_pBase;
+
+ int m_nLogicalBytes;
+ int m_nActualBytes;
+ int m_nAllocs;
+ int m_nOldBytes;
+ int m_nNonTextureAllocs;
+ int m_nBytesTotal;
+};
+
+//-----------------------------------------------------------------------------
+
+inline TextureAllocator_t GetTextureAllocator( IDirect3DBaseTexture9 *pTexture )
+{
+ return ( pTexture->GetType() == D3DRTYPE_CUBETEXTURE ) ? (( CXboxCubeTexture *)pTexture)->m_fAllocator : (( CXboxTexture *)pTexture)->m_fAllocator;
+}
+
+//-----------------------------------------------------------------------------
+
+CMixedTextureHeap g_MixedTextureHeap;
+
+CON_COMMAND( mat_texture_heap_stats, "" )
+{
+ if ( UseStandardAllocator() )
+ {
+ Msg( "Texture heap stats: (Standard Allocator)\n" );
+ Msg( "Allocations:%d Size:%d\n", CD3DTextureAllocator::GetAllocations(), CD3DTextureAllocator::GetSize() );
+ }
+ else
+ {
+ Msg( "Texture heap stats:\n" );
+ Msg( " Mixed textures: %dk/%dk allocated in %d textures\n", g_MixedTextureHeap.m_nLogicalBytes/1024, g_MixedTextureHeap.m_nActualBytes/1024, g_MixedTextureHeap.m_nAllocs );
+ float oldFootprint = g_MixedTextureHeap.m_nOldBytes;
+ float newFootprint = g_MixedTextureHeap.m_nActualBytes;
+ Msg( "\n Old: %.3fmb, New: %.3fmb\n", oldFootprint / (1024.0*1024.0), newFootprint / (1024.0*1024.0) );
+ }
+}
+
+CON_COMMAND( mat_texture_heap_compact, "" )
+{
+ Msg( "Validating texture heap...\n" );
+ g_MixedTextureHeap.Validate();
+ Msg( "Compacting texture heap...\n" );
+ unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
+ g_MixedTextureHeap.Compact();
+ unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
+
+ Msg( "\n Old largest block: %.3fk, New largest block: %.3fk\n\n", oldLargest / 1024.0, newLargest / 1024.0 );
+
+ Msg( "Validating texture heap...\n" );
+ g_MixedTextureHeap.Validate();
+ Msg( "Done.\n" );
+}
+
+//-----------------------------------------------------------------------------
+// Nasty back doors
+//-----------------------------------------------------------------------------
+
+void CompactTextureHeap()
+{
+ unsigned oldLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
+ g_MixedTextureHeap.Compact();
+ unsigned newLargest = ( g_MixedTextureHeap.GetLastFree() ) ? g_MixedTextureHeap.GetLastFree()->heapInfo.nBytes : 0;
+
+ DevMsg( "Compacted texture heap. Old largest block: %.3fk, New largest block: %.3fk\n", oldLargest / 1024.0, newLargest / 1024.0 );
+}
+
+CTextureHeap g_TextureHeap;
+
+//-----------------------------------------------------------------------------
+// Build and alloc a texture resource
+//-----------------------------------------------------------------------------
+IDirect3DTexture *CTextureHeap::AllocTexture( int width, int height, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory )
+{
+ CXboxTexture* pD3DTexture = new CXboxTexture;
+
+ // create a texture with contiguous mips and packed tails
+ DWORD dwTextureSize = XGSetTextureHeaderEx(
+ width,
+ height,
+ levels,
+ usage,
+ d3dFormat,
+ 0,
+ 0,
+ 0,
+ XGHEADER_CONTIGUOUS_MIP_OFFSET,
+ 0,
+ pD3DTexture,
+ NULL,
+ NULL );
+
+ // based on "Xbox 360 Texture Storage"
+ // can truncate the terminal tile using packed tails
+ // the terminal tile must be at 32x32 or 16x16 packed
+ if ( width == height && levels != 0 )
+ {
+ int terminalWidth = width >> (levels - 1);
+ if ( d3dFormat == D3DFMT_DXT1 )
+ {
+ if ( terminalWidth <= 32 )
+ {
+ dwTextureSize -= 4*1024;
+ }
+ }
+ else if ( d3dFormat == D3DFMT_DXT5 )
+ {
+ if ( terminalWidth == 32 )
+ {
+ dwTextureSize -= 8*1024;
+ }
+ else if ( terminalWidth <= 16 )
+ {
+ dwTextureSize -= 12*1024;
+ }
+ }
+ }
+
+ pD3DTexture->m_TextureSize = dwTextureSize;
+
+ if ( !bFallback && bNoD3DMemory )
+ {
+ pD3DTexture->m_fAllocator = TA_UNKNOWN;
+ return pD3DTexture;
+ }
+
+ void *pBuffer;
+ if ( UseStandardAllocator() )
+ {
+ MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" );
+ pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize );
+ pD3DTexture->m_fAllocator = TA_DEFAULT;
+ }
+ else
+ {
+ MEM_ALLOC_CREDIT_( __FILE__ ": Mixed texture" );
+ pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture );
+ if ( pBuffer )
+ {
+ pD3DTexture->m_fAllocator = TA_MIXED;
+ }
+ else
+ {
+ g_MixedTextureHeap.Compact();
+ pBuffer = g_MixedTextureHeap.Alloc( dwTextureSize, pD3DTexture );
+ if ( pBuffer )
+ {
+ pD3DTexture->m_fAllocator = TA_MIXED;
+ }
+ else
+ {
+ pBuffer = CD3DTextureAllocator::Alloc( dwTextureSize );
+ pD3DTexture->m_fAllocator = TA_DEFAULT;
+ }
+ }
+ }
+
+ if ( !pBuffer )
+ {
+ delete pD3DTexture;
+ return NULL;
+ }
+
+ XGOffsetResourceAddress( pD3DTexture, pBuffer );
+
+ return pD3DTexture;
+}
+
+//-----------------------------------------------------------------------------
+// Build and alloc a cube texture resource
+//-----------------------------------------------------------------------------
+IDirect3DCubeTexture *CTextureHeap::AllocCubeTexture( int width, int levels, DWORD usage, D3DFORMAT d3dFormat, bool bFallback, bool bNoD3DMemory )
+{
+ CXboxCubeTexture* pD3DCubeTexture = new CXboxCubeTexture;
+
+ // create a cube texture with contiguous mips and packed tails
+ DWORD dwTextureSize = XGSetCubeTextureHeaderEx(
+ width,
+ levels,
+ usage,
+ d3dFormat,
+ 0,
+ 0,
+ 0,
+ XGHEADER_CONTIGUOUS_MIP_OFFSET,
+ pD3DCubeTexture,
+ NULL,
+ NULL );
+ pD3DCubeTexture->m_TextureSize = dwTextureSize;
+
+ if ( !bFallback && bNoD3DMemory )
+ {
+ pD3DCubeTexture->m_fAllocator = TA_UNKNOWN;
+ return pD3DCubeTexture;
+ }
+
+ void *pBits;
+ if ( UseStandardAllocator() )
+ {
+ MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" );
+ pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
+ pD3DCubeTexture->m_fAllocator = TA_DEFAULT;
+ }
+ else
+ {
+ // @todo: switch to texture heap
+ MEM_ALLOC_CREDIT_( __FILE__ ": Odd sized cubemap textures" );
+ // Really only happens with environment map
+ pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
+ pD3DCubeTexture->m_fAllocator = TA_DEFAULT;
+ }
+
+ if ( !pBits )
+ {
+ delete pD3DCubeTexture;
+ return NULL;
+ }
+
+ XGOffsetResourceAddress( pD3DCubeTexture, pBits );
+
+ return pD3DCubeTexture;
+}
+
+//-----------------------------------------------------------------------------
+// Allocate an Volume Texture
+//-----------------------------------------------------------------------------
+IDirect3DVolumeTexture *CTextureHeap::AllocVolumeTexture( int width, int height, int depth, int levels, DWORD usage, D3DFORMAT d3dFormat )
+{
+ CXboxVolumeTexture *pD3DVolumeTexture = new CXboxVolumeTexture;
+
+ // create a cube texture with contiguous mips and packed tails
+ DWORD dwTextureSize = XGSetVolumeTextureHeaderEx(
+ width,
+ height,
+ depth,
+ levels,
+ usage,
+ d3dFormat,
+ 0,
+ 0,
+ 0,
+ XGHEADER_CONTIGUOUS_MIP_OFFSET,
+ pD3DVolumeTexture,
+ NULL,
+ NULL );
+
+ void *pBits;
+
+ MEM_ALLOC_CREDIT_( __FILE__ ": Volume standard D3D" );
+
+ pBits = CD3DTextureAllocator::Alloc( dwTextureSize );
+ pD3DVolumeTexture->m_fAllocator = TA_DEFAULT;
+ pD3DVolumeTexture->m_TextureSize = dwTextureSize;
+
+ if ( !pBits )
+ {
+ delete pD3DVolumeTexture;
+ return NULL;
+ }
+
+ XGOffsetResourceAddress( pD3DVolumeTexture, pBits );
+
+ return pD3DVolumeTexture;
+}
+
+//-----------------------------------------------------------------------------
+// Get current backbuffer multisample type (used in AllocRenderTargetSurface() )
+//-----------------------------------------------------------------------------
+D3DMULTISAMPLE_TYPE CTextureHeap::GetBackBufferMultiSampleType()
+{
+ int backWidth, backHeight;
+ ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
+
+ // 2xMSAA at 640x480 and 848x480 are the only supported multisample mode on 360 (2xMSAA for 720p would
+ // use predicated tiling, which would require a rewrite of *all* our render target code)
+ // FIXME: shuffle the EDRAM surfaces to allow 4xMSAA for standard def
+ // (they would overlap & trash each other with the current allocation scheme)
+ D3DMULTISAMPLE_TYPE backBufferMultiSampleType = g_pShaderDevice->IsAAEnabled() ? D3DMULTISAMPLE_2_SAMPLES : D3DMULTISAMPLE_NONE;
+ Assert( ( g_pShaderDevice->IsAAEnabled() == false ) || (backHeight == 480) );
+
+ return backBufferMultiSampleType;
+}
+
+//-----------------------------------------------------------------------------
+// Allocate an EDRAM surface
+//-----------------------------------------------------------------------------
+IDirect3DSurface *CTextureHeap::AllocRenderTargetSurface( int width, int height, D3DFORMAT d3dFormat, bool bMultiSample, int base )
+{
+ // render target surfaces don't need to exist simultaneously
+ // force their allocations to overlap at the end of back buffer and zbuffer
+ // this should leave 3MB (of 10) free assuming 1280x720 (and 5MB with 640x480@2xMSAA)
+ D3DMULTISAMPLE_TYPE backBufferMultiSampleType = GetBackBufferMultiSampleType();
+ D3DMULTISAMPLE_TYPE multiSampleType = bMultiSample ? backBufferMultiSampleType : D3DMULTISAMPLE_NONE;
+ if ( base < 0 )
+ {
+ int backWidth, backHeight;
+ ShaderAPI()->GetBackBufferDimensions( backWidth, backHeight );
+ D3DFORMAT backBufferFormat = ImageLoader::ImageFormatToD3DFormat( g_pShaderDevice->GetBackBufferFormat() );
+ base = 2*XGSurfaceSize( backWidth, backHeight, backBufferFormat, backBufferMultiSampleType );
+ }
+
+ D3DSURFACE_PARAMETERS surfParameters;
+ surfParameters.Base = base;
+ surfParameters.ColorExpBias = 0;
+
+ if ( ( d3dFormat == D3DFMT_D24FS8 ) || ( d3dFormat == D3DFMT_D24S8 ) || ( d3dFormat == D3DFMT_D16 ) )
+ {
+ surfParameters.HierarchicalZBase = 0;
+ if ( ( surfParameters.HierarchicalZBase + XGHierarchicalZSize( width, height, multiSampleType ) ) > GPU_HIERARCHICAL_Z_TILES )
+ {
+ // overflow, can't hold the tiles so disable
+ surfParameters.HierarchicalZBase = 0xFFFFFFFF;
+ }
+ }
+ else
+ {
+ // not using
+ surfParameters.HierarchicalZBase = 0xFFFFFFFF;
+ }
+
+ HRESULT hr;
+ IDirect3DSurface9 *pSurface = NULL;
+ hr = Dx9Device()->CreateRenderTarget( width, height, d3dFormat, multiSampleType, 0, FALSE, &pSurface, &surfParameters );
+ Assert( !FAILED( hr ) );
+
+ return pSurface;
+}
+
+//-----------------------------------------------------------------------------
+// Perform the real d3d allocation, returns true if succesful, false otherwise.
+// Only valid for a texture created with no d3d bits, otherwise no-op.
+//-----------------------------------------------------------------------------
+bool CTextureHeap::AllocD3DMemory( IDirect3DBaseTexture *pD3DTexture )
+{
+ if ( !pD3DTexture )
+ {
+ return false;
+ }
+
+ if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
+ {
+ // there are no d3d bits for a surface
+ return false;
+ }
+
+ void *pBits = GetD3DTextureBasePtr( pD3DTexture );
+ if ( pBits )
+ {
+ // already have d3d bits
+ return true;
+ }
+
+ if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
+ {
+ MEM_ALLOC_CREDIT_( __FILE__ ": Standard D3D" );
+ pBits = CD3DTextureAllocator::Alloc( ((CXboxTexture *)pD3DTexture)->m_TextureSize );
+ ((CXboxTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT;
+ XGOffsetResourceAddress( (CXboxTexture *)pD3DTexture, pBits );
+ return true;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
+ {
+ MEM_ALLOC_CREDIT_( __FILE__ ": Cubemap standard D3D" );
+ pBits = CD3DTextureAllocator::Alloc( ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize );
+ ((CXboxCubeTexture *)pD3DTexture)->m_fAllocator = TA_DEFAULT;
+ XGOffsetResourceAddress( (CXboxCubeTexture *)pD3DTexture, pBits );
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Release the allocated store
+//-----------------------------------------------------------------------------
+void CTextureHeap::FreeTexture( IDirect3DBaseTexture *pD3DTexture )
+{
+ if ( !pD3DTexture )
+ {
+ return;
+ }
+
+ if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
+ {
+ // texture heap doesn't own render target surfaces
+ // allow callers to call through for less higher level detection
+ int ref = ((IDirect3DSurface*)pD3DTexture)->Release();
+ Assert( ref == 0 );
+ ref = ref; // Quiet "unused variable" warning in release
+ return;
+ }
+ else
+ {
+ byte *pBits = (byte *)GetD3DTextureBasePtr( pD3DTexture );
+ if ( pBits )
+ {
+ switch ( GetTextureAllocator( pD3DTexture ) )
+ {
+ case TA_DEFAULT:
+ CD3DTextureAllocator::Free( pBits );
+ break;
+
+ case TA_MIXED:
+ g_MixedTextureHeap.Free( pBits, ((CXboxTexture *)pD3DTexture) );
+ break;
+ }
+ }
+ }
+
+ if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
+ {
+ delete (CXboxTexture *)pD3DTexture;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE )
+ {
+ delete (CXboxVolumeTexture *)pD3DTexture;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
+ {
+ delete (CXboxCubeTexture *)pD3DTexture;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Returns the allocated footprint
+//-----------------------------------------------------------------------------
+int CTextureHeap::GetSize( IDirect3DBaseTexture *pD3DTexture )
+{
+ if( pD3DTexture == NULL )
+ return 0;
+
+ if ( pD3DTexture->GetType() == D3DRTYPE_SURFACE )
+ {
+ D3DSURFACE_DESC surfaceDesc;
+ HRESULT hr = ((IDirect3DSurface*)pD3DTexture)->GetDesc( &surfaceDesc );
+ Assert( !FAILED( hr ) );
+ hr = hr; // Quiet "unused variable" warning in release
+
+ int size = ImageLoader::GetMemRequired(
+ surfaceDesc.Width,
+ surfaceDesc.Height,
+ 0,
+ ImageLoader::D3DFormatToImageFormat( surfaceDesc.Format ),
+ false );
+
+ return size;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_TEXTURE )
+ {
+ return ((CXboxTexture *)pD3DTexture)->m_TextureSize;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_CUBETEXTURE )
+ {
+ return ((CXboxCubeTexture *)pD3DTexture)->m_TextureSize;
+ }
+ else if ( pD3DTexture->GetType() == D3DRTYPE_VOLUMETEXTURE )
+ {
+ return ((CXboxVolumeTexture *)pD3DTexture)->m_TextureSize;
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Crunch the pools
+//-----------------------------------------------------------------------------
+void CTextureHeap::Compact()
+{
+ g_MixedTextureHeap.Compact();
+}