Leaked source code of windows server 2003
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.
 
 
 
 
 
 

442 lines
17 KiB

#include "pch.cpp"
#pragma hdrstop
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager"
TextureCacheManager::TextureCacheManager(LPDIRECT3DI lpD3DI)
{
for(int i = 0; i < MAXLOGTEXSIZE; tcm_bucket[i++] = NULL);
tcm_ticks = 0;
numvidtex = 0;
lpDirect3DI=lpD3DI;
}
TextureCacheManager::~TextureCacheManager()
{
for(int i = 0; i < MAXLOGTEXSIZE;i++){
LPD3DBUCKET bucket=tcm_bucket[i];
while(bucket){
tcm_bucket[i]=bucket->next;
if (bucket->lpD3DTexI) remove(bucket);
D3DFreeBucket(lpDirect3DI,bucket);
bucket=tcm_bucket[i];
}
}
}
BOOL TextureCacheManager::freeNode(LPD3DI_TEXTUREBLOCK lpBlock, LPD3DBUCKET* lplpBucket)
{
// Starting from the current size, find the LRU texture and remove it.
unsigned int oldest = tcm_ticks;
LPD3DBUCKET bucket,last,target=NULL;
int k=lpBlock->lpD3DTextureI->LogTexSize;
DWORD dwTextureStage=lpBlock->lpD3DTextureI->ddsd.dwTextureStage;
BOOL bSingleMemory = !(lpBlock->lpDevI->lpD3DHALGlobalDriverData->hwCaps.dwDevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES);
D3D_INFO(8,"freeing size %d bSingleMemory=%d",k,bSingleMemory);
for(int i = k; i < MAXLOGTEXSIZE; ++i) {
for(bucket=tcm_bucket[i],last=NULL; bucket;){
if (!bucket->lpD3DTexI){ //invalidated by Tex3I destructors ?
LPD3DBUCKET temp=bucket->next;
D3DFreeBucket(lpDirect3DI,bucket);
bucket=temp;
if (last){ // Yes, but is this the first node on the list
last->next=bucket;
}
else
{
tcm_bucket[i]=bucket;
}
continue;
}
if(bucket->ticks < oldest &&
(bSingleMemory || (bucket->lpD3DTexI->ddsd.dwTextureStage == dwTextureStage)) &&
!bucket->lpD3DTexI->bInUse)
{
oldest = bucket->ticks;
target = bucket;
}
last=bucket;
bucket=last->next;
}
if (oldest != tcm_ticks)
{
if (i==k &&
!(lpBlock->lpD3DTextureI->ddsd.ddsCaps.dwCaps2 &
(DDSCAPS2_HINTSTATIC | DDSCAPS2_OPAQUE)
) &&
!(target->lpD3DTexI->ddsd.ddsCaps.dwCaps2 &
(DDSCAPS2_HINTSTATIC | DDSCAPS2_OPAQUE)
) &&
!(!(lpBlock->lpD3DTextureI->ddsd.ddsCaps.dwCaps & DDSCAPS_MIPMAP) && (target->lpD3DTexI->ddsd.ddsCaps.dwCaps & DDSCAPS_MIPMAP)
) &&
!((DDPF_PALETTEINDEXED1 |
DDPF_PALETTEINDEXED2 | DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED8)
& lpBlock->lpD3DTextureI->ddsd.ddpfPixelFormat.dwFlags)) //workaround of ddraw not updating surface pallete
*lplpBucket=target; //possible candidate for replace
else
remove(target);
return TRUE;
}
}
// If bigger size texture not found, try smaller sizes.
for(i = k - 1; i > 0; --i) {
for(bucket=tcm_bucket[i],last=NULL; bucket;){
if (!bucket->lpD3DTexI){ //invalidated by TexI destructors ?
LPD3DBUCKET temp=bucket->next;
D3DFreeBucket(lpDirect3DI,bucket);
bucket=temp;
if (last){ // Yes, but is this the first node on the list
last->next=bucket;
}
else
{
tcm_bucket[i]=bucket;
}
continue;
}
if(bucket->ticks < oldest &&
(bSingleMemory || (bucket->lpD3DTexI->ddsd.dwTextureStage == dwTextureStage)) &&
!bucket->lpD3DTexI->bInUse)
{
oldest = bucket->ticks;
target = bucket;
}
last=bucket;
bucket=last->next;
}
if (oldest != tcm_ticks)
{
remove(target);
return TRUE;
}
}
return FALSE;
}
HRESULT MarkDirtyPointers(LPDIRECT3DTEXTUREI lpD3DTexI)
{
// Next, we need to loop thru and set pointers to the dirty
// bit in the DDraw surfaces
HRESULT ddrval;
DDSCAPS2 ddscaps;
LPDIRECTDRAWSURFACE4 lpDDSTmp, lpDDS = lpD3DTexI->lpDDS, lpDDSSys = lpD3DTexI->lpDDSSys;
do
{
((LPDDRAWI_DDRAWSURFACE_INT) lpDDS)->lpLcl->lpSurfMore->lpbDirty = &(lpD3DTexI->bDirty);
((LPDDRAWI_DDRAWSURFACE_INT) lpDDS)->lpLcl->lpSurfMore->lpRegionList = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSSys)->lpLcl->lpSurfMore->lpRegionList;
memset(&ddscaps, 0, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
ddrval = lpDDS->GetAttachedSurface(&ddscaps, &lpDDSTmp);
if (lpDDS != lpD3DTexI->lpDDS) lpDDS->Release();
lpDDS = lpDDSTmp;
if (DDERR_NOTFOUND == ddrval)
{
if (lpDDSSys != lpD3DTexI->lpDDSSys) lpDDSSys->Release();
return D3D_OK;
}
else if(DD_OK != ddrval)
{
D3D_ERR("GetAttachedSurface failed unexpectedly in MarkDirtyPointers");
if (lpDDSSys != lpD3DTexI->lpDDSSys) lpDDSSys->Release();
return ddrval;
}
memset(&ddscaps, 0, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
ddrval = lpDDSSys->GetAttachedSurface(&ddscaps, &lpDDSTmp);
if (lpDDSSys != lpD3DTexI->lpDDSSys) lpDDSSys->Release();
lpDDSSys = lpDDSTmp;
}
while(ddrval == DD_OK);
D3D_ERR("GetAttachedSurface failed unexpectedly in MarkDirtyPointers");
if (lpDDS != lpD3DTexI->lpDDS) lpDDS->Release();
return ddrval;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::allocNode"
HRESULT TextureCacheManager::allocNode(LPD3DI_TEXTUREBLOCK lpBlock)
{
LPD3DBUCKET lpCachedTexture=NULL;
DWORD pcaps,dwCaps;
HRESULT ddrval;
LPDIRECT3DTEXTUREI lpD3DTexI=lpBlock->lpD3DTextureI;
DDASSERT(!lpBlock->hTex && lpD3DTexI && lpBlock->lpDevI);
if (0 == lpD3DTexI->LogTexSize)
{
DWORD texsize; // in the number of half bytes
// Compute log2 of the texture size. This is the bucket index.
memset(&lpD3DTexI->ddsd, 0, sizeof(lpD3DTexI->ddsd));
lpD3DTexI->ddsd.dwSize = sizeof(lpD3DTexI->ddsd);
if (DD_OK != (ddrval=lpD3DTexI->lpDDSSys->GetSurfaceDesc(&lpD3DTexI->ddsd)))
{
D3D_WARN(1,"GetSurfaceDesc failed in TextureCacheManager::allocNode");
return ddrval;
}
if (DDSD_LINEARSIZE & lpD3DTexI->ddsd.dwFlags)
{
texsize = lpD3DTexI->ddsd.dwLinearSize*2;
}
else
{
texsize = lpD3DTexI->ddsd.dwWidth*lpD3DTexI->ddsd.dwHeight*lpD3DTexI->ddsd.ddpfPixelFormat.dwRGBBitCount/4;
}
if (0 != texsize )
{
for(; (texsize & 1) == 0; texsize >>= 1) ++lpD3DTexI->LogTexSize;
}
else
lpD3DTexI->LogTexSize=1;
D3D_INFO(7,"Managed Texture size=%d",lpD3DTexI->LogTexSize);
DDASSERT(lpD3DTexI->LogTexSize < MAXLOGTEXSIZE );
lpD3DTexI->ddsd.dwFlags &= ~DDSD_PITCH; // DDRAW always give that, but we don't want
lpD3DTexI->ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
lpD3DTexI->ddsd.ddsCaps.dwCaps2 &= ~(DDSCAPS2_TEXTUREMANAGE);
lpD3DTexI->ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
}
// Attempt to allocate a texture. If out of video memory, first try to replace
// an old texture. If replacement not possible, free old textures using an LRU scheme
// till enough memory is available.
do {
ddrval = lpDirect3DI->lpDD4->CreateSurface(&lpD3DTexI->ddsd, &lpD3DTexI->lpDDS, NULL);
if (DD_OK== ddrval) { // No problem, there is enough memory.
if (D3D_OK != (ddrval=MarkDirtyPointers(lpD3DTexI)))
{
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
return ddrval;
}
if (D3D_OK != (ddrval=D3DMallocBucket(lpDirect3DI,&lpCachedTexture)))
{
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
return ddrval;
}
lpCachedTexture->next=tcm_bucket[lpD3DTexI->LogTexSize];
lpCachedTexture->lpD3DTexI=lpD3DTexI;
lpCachedTexture->ticks=tcm_ticks;
tcm_bucket[lpD3DTexI->LogTexSize]=lpCachedTexture;
++numvidtex;
}
else
if(ddrval == DDERR_OUTOFVIDEOMEMORY) { // If out of video memory
if (freeNode(lpBlock,&lpCachedTexture))
{
if (lpCachedTexture)
{ //found a replace candidate
replace(lpCachedTexture,lpBlock);
if ( lpD3DTexI->ddsd.dwWidth == lpCachedTexture->lpD3DTexI->ddsd.dwWidth
&& lpD3DTexI->ddsd.dwHeight == lpCachedTexture->lpD3DTexI->ddsd.dwHeight
&& lpD3DTexI->ddsd.ddpfPixelFormat.dwRBitMask == lpCachedTexture->lpD3DTexI->ddsd.ddpfPixelFormat.dwRBitMask
&& lpD3DTexI->ddsd.ddpfPixelFormat.dwFlags == lpCachedTexture->lpD3DTexI->ddsd.ddpfPixelFormat.dwFlags
)
{
lpD3DTexI->lpDDS = lpCachedTexture->lpD3DTexI->lpDDS;
if (DD_OK == CopySurface(lpD3DTexI->lpDDS,lpD3DTexI->lpDDSSys,NULL)
&& D3D_OK == MarkDirtyPointers(lpD3DTexI) )
{
lpCachedTexture->lpD3DTexI->lpDDS=NULL;
lpD3DTexI->lpTMBucket=lpCachedTexture;
lpCachedTexture->lpD3DTexI->lpTMBucket=NULL;
lpD3DTexI->lpDDS1Tex = lpCachedTexture->lpD3DTexI->lpDDS1Tex;
lpCachedTexture->lpD3DTexI->lpDDS1Tex=NULL;
lpCachedTexture->lpD3DTexI=lpD3DTexI;
lpCachedTexture->ticks=tcm_ticks;
lpD3DTexI->bDirty=FALSE;
return D3D_OK;
}
lpD3DTexI->lpDDS = NULL;
D3D_WARN(2,"(%d %d %d) matching replace failed in TextureCacheManager::allocNode lpD3DTexI=%08lx with lpDDS=%08lx",
lpD3DTexI->LogTexSize,lpD3DTexI->ddsd.dwWidth,lpD3DTexI->ddsd.dwHeight,lpD3DTexI,lpCachedTexture->lpD3DTexI->lpDDS);
}
if (lpBlock->hTex) //copy failed, therefore clear handle also
{
CLockD3DST lockObject(lpBlock->lpDevI, DPF_MODNAME, REMIND(""));
D3DHAL_TextureDestroy(lpBlock);
}
remove(lpCachedTexture);
lpCachedTexture=NULL;
}
}
else
{
D3D_ERR("all Freed no further video memory available");
#if DBG
D3D_INFO(4,"freeing size %d dwTextureStage=%d tcm_ticks=%08lx dwCaps =%08lx failed",
lpD3DTexI->LogTexSize,lpD3DTexI->ddsd.dwTextureStage,tcm_ticks,lpD3DTexI->ddsd.ddsCaps.dwCaps);
for(int i = 0; i < MAXLOGTEXSIZE; ++i)
{
if (tcm_bucket[i]) D3D_INFO(5,"List of size %d",i);
for(LPD3DBUCKET bucket=tcm_bucket[i];bucket;bucket=bucket->next)
{
if (bucket->lpD3DTexI)
D3D_INFO(5,"ticks=%08lx lpD3DTexI=%08lx InUse=%08lx dwTextureStage=%d",
bucket->ticks,bucket->lpD3DTexI,
bucket->lpD3DTexI->bInUse,
bucket->lpD3DTexI->ddsd.dwTextureStage);
}
}
#endif
return DDERR_OUTOFVIDEOMEMORY; //nothing left
}
}
else{
D3D_ERR("Unexpected error got in allocNode");
return ddrval;
}
}while(ddrval == DDERR_OUTOFVIDEOMEMORY);
if (lpD3DTexI->ddsd.ddpfPixelFormat.dwFlags & (DDPF_PALETTEINDEXED8 | DDPF_PALETTEINDEXED4)) {
LPDIRECTDRAWPALETTE lpDDPal;
if (DD_OK != (ddrval=lpD3DTexI->lpDDSSys->GetPalette(&lpDDPal))) {
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
lpCachedTexture->lpD3DTexI=NULL;
D3D_ERR("failed to check for palette on texture");
return ddrval;
}
if (DD_OK != (ddrval=lpD3DTexI->lpDDS->SetPalette(lpDDPal))){
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
lpCachedTexture->lpD3DTexI=NULL;
D3D_ERR("SetPalette returned error");
return ddrval;
}
lpDDPal->Release();
}
if (DD_OK != (ddrval=CopySurface(lpD3DTexI->lpDDS,lpD3DTexI->lpDDSSys,NULL))){
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
lpCachedTexture->lpD3DTexI=NULL;
D3D_ERR("CopySurface returned error");
return ddrval;
}
lpD3DTexI->bDirty=FALSE;
lpD3DTexI->lpTMBucket=lpCachedTexture;
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::remove"
//remove all HW handles and release surface
void TextureCacheManager::remove(LPD3DBUCKET bucket)
{
LPDIRECT3DTEXTUREI lpD3DTexI=bucket->lpD3DTexI;
LPD3DI_TEXTUREBLOCK tBlock = LIST_FIRST(&lpD3DTexI->blocks);
while(tBlock){
if (tBlock->hTex)
{
CLockD3DST lockObject(tBlock->lpDevI, DPF_MODNAME, REMIND(""));
D3DHAL_TextureDestroy(tBlock);
}
tBlock=LIST_NEXT(tBlock,list);
}
D3D_INFO(7,"removing lpD3DTexI=%08lx lpDDS=%08lx",lpD3DTexI,lpD3DTexI->lpDDS);
lpD3DTexI->lpDDS1Tex->Release();
lpD3DTexI->lpDDS1Tex=NULL;
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS=NULL;
lpD3DTexI->lpTMBucket=NULL;
bucket->lpD3DTexI=NULL;
--numvidtex;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::replace"
//remove any HW handle that's not with lpBlock->lpDevI,save lpBlock->hTex,keep surface
void TextureCacheManager::replace(LPD3DBUCKET bucket,LPD3DI_TEXTUREBLOCK lpBlock)
{
LPD3DI_TEXTUREBLOCK tBlock = LIST_FIRST(&bucket->lpD3DTexI->blocks);
while(tBlock){
if (tBlock->hTex)
{
if (tBlock->lpDevI == lpBlock->lpDevI)
{
lpBlock->hTex=tBlock->hTex; //save it
//flush before copy
LPD3DBUCKET list = reinterpret_cast<LPD3DBUCKET>(((LPDDRAWI_DDRAWSURFACE_INT)(tBlock->lpD3DTextureI->lpDDS))->lpLcl->lpSurfMore->lpD3DDevIList);
while(list != NULL)
{
if(list->lpD3DDevI == tBlock->lpDevI)
{
if(tBlock->lpDevI->FlushStates() != D3D_OK)
{
D3D_ERR("Error trying to render batched commands in TextureCacheManager::replace");
}
break;
}
list = list->next;
}
tBlock->hTex=0;
}
else
{
CLockD3DST lockObject(tBlock->lpDevI, DPF_MODNAME, REMIND(""));
D3DHAL_TextureDestroy(tBlock);
}
}
tBlock=LIST_NEXT(tBlock,list);
}
D3D_INFO(7,"replacing lpDDS=%08lx from lpD3DTexI=%08lx",bucket->lpD3DTexI->lpDDS,bucket->lpD3DTexI);
}
void TextureCacheManager::EvictTextures()
{
for(int i = 0; i < MAXLOGTEXSIZE;i++){
LPD3DBUCKET bucket=tcm_bucket[i];
while(bucket){
if (bucket->lpD3DTexI)
{
remove(bucket);
}
bucket=bucket->next;
}
}
}
void TextureCacheManager::cleanup()
{
for(int i = 0; i < MAXLOGTEXSIZE;i++){
for(LPD3DBUCKET bucket=tcm_bucket[i],last=NULL; bucket;){
if (!bucket->lpD3DTexI){ //invalidated by Tex3I destructors ?
LPD3DBUCKET temp=bucket->next;
D3DFreeBucket(lpDirect3DI,bucket);
bucket=temp;
if (last){ // Yes, but is this the first node on the list
last->next=bucket;
}
else
{
tcm_bucket[i]=bucket;
}
}
else{
last=bucket;
bucket=last->next;
}
}
}
}
BOOL TextureCacheManager::CheckIfLost()
{
if(numvidtex)
{
for(int i = 0; i < MAXLOGTEXSIZE; ++i)
{
for(LPD3DBUCKET bucket = tcm_bucket[i]; bucket; bucket = bucket->next)
{
if(bucket->lpD3DTexI)
{
if(((LPDDRAWI_DDRAWSURFACE_INT)(bucket->lpD3DTexI->lpDDS))->lpLcl->dwFlags & DDRAWISURF_INVALID)
return TRUE;
else
return FALSE;
}
}
}
}
return FALSE;
}