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
1258 lines
30 KiB
//========= 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(); |
|
}
|
|
|