//========= 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(); }