hl2_src-leak-2017/src/materialsystem/shaderapidx9/textureheap.cpp

1259 lines
30 KiB
C++

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