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