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.
 
 
 
 
 
 

593 lines
18 KiB

#include "pch.cpp"
#pragma hdrstop
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::TextureHeap"
TextureHeap::TextureHeap(DWORD size)
{
m_next = 1;
m_size = size + 1;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::~TextureHeap"
TextureHeap::~TextureHeap()
{
delete[] m_data_p;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::Initialize"
bool TextureHeap::Initialize()
{
m_data_p = new LPDIRECT3DTEXTUREI[m_size];
if(m_data_p == 0)
{
D3D_ERR("Failed to allocate texture heap.");
return false;
}
memset(m_data_p, 0, sizeof(LPDIRECT3DTEXTUREI) * m_size);
return true;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::heapify"
void TextureHeap::heapify(DWORD k)
{
while(true)
{
DWORD smallest;
DWORD l = lchild(k);
DWORD r = rchild(k);
if(l < m_next)
if(m_data_p[l]->Cost() < m_data_p[k]->Cost())
smallest = l;
else
smallest = k;
else
smallest = k;
if(r < m_next)
if(m_data_p[r]->Cost() < m_data_p[smallest]->Cost())
smallest = r;
if(smallest != k)
{
LPDIRECT3DTEXTUREI t = m_data_p[k];
m_data_p[k] = m_data_p[smallest];
m_data_p[k]->m_dwHeapIndex = k;
m_data_p[smallest] = t;
m_data_p[smallest]->m_dwHeapIndex = smallest;
k = smallest;
}
else
break;
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::add"
bool TextureHeap::add(LPDIRECT3DTEXTUREI lpD3DTexI)
{
if(m_next == m_size)
{
m_size = m_size * 2 - 1;
LPDIRECT3DTEXTUREI *p = new LPDIRECT3DTEXTUREI[m_size];
if(p == 0)
{
D3D_ERR("Failed to allocate memory to grow heap.");
m_size = (m_size + 1) / 2; // restore size
return false;
}
memcpy(p + 1, m_data_p + 1, sizeof(LPDIRECT3DTEXTUREI) * (m_next - 1));
delete[] m_data_p;
m_data_p = p;
}
ULONGLONG Cost = lpD3DTexI->Cost();
for(DWORD k = m_next; k > 1; k = parent(k))
if(Cost < m_data_p[parent(k)]->Cost())
{
m_data_p[k] = m_data_p[parent(k)];
m_data_p[k]->m_dwHeapIndex = k;
}
else
break;
m_data_p[k] = lpD3DTexI;
m_data_p[k]->m_dwHeapIndex = k;
++m_next;
return true;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::extractMin"
LPDIRECT3DTEXTUREI TextureHeap::extractMin()
{
LPDIRECT3DTEXTUREI lpD3DTexI = m_data_p[1];
--m_next;
m_data_p[1] = m_data_p[m_next];
m_data_p[1]->m_dwHeapIndex = 1;
heapify(1);
lpD3DTexI->m_dwHeapIndex = 0;
return lpD3DTexI;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::extractMax"
LPDIRECT3DTEXTUREI TextureHeap::extractMax()
{
// When extracting the max element from the heap, we don't need to
// search the entire heap, but just the leafnodes. This is because
// it is guaranteed that parent nodes are cheaper than the leaf nodes
// so once you have looked through the leaves, you won't find anything
// cheaper.
// NOTE: (lchild(i) >= m_next) is true only for leaf nodes.
// ALSO NOTE: You cannot have a rchild without a lchild, so simply
// checking for lchild is sufficient.
unsigned max = m_next - 1;
ULONGLONG maxcost = 0;
for(unsigned i = max; lchild(i) >= m_next; --i)
{
ULONGLONG Cost = m_data_p[i]->Cost();
if(maxcost < Cost)
{
maxcost = Cost;
max = i;
}
}
LPDIRECT3DTEXTUREI lpD3DTexI = m_data_p[max];
if(lpD3DTexI->m_bInUse)
{
max = 0;
maxcost = 0;
for(i = m_next - 1; i > 0; --i)
{
ULONGLONG Cost = m_data_p[i]->Cost();
if(maxcost < Cost && !m_data_p[i]->m_bInUse)
{
maxcost = Cost;
max = i;
}
}
if(max == 0) // All textures in use
return NULL;
lpD3DTexI = m_data_p[max];
}
del(max);
return lpD3DTexI;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::extractNotInScene"
LPDIRECT3DTEXTUREI TextureHeap::extractNotInScene(DWORD dwScene)
{
for(unsigned i = 1; i < m_next; ++i)
{
if(m_data_p[i]->m_dwScene != dwScene)
{
LPDIRECT3DTEXTUREI lpD3DTexI = m_data_p[i];
del(i);
return lpD3DTexI;
}
}
return NULL;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::del"
void TextureHeap::del(DWORD k)
{
LPDIRECT3DTEXTUREI lpD3DTexI = m_data_p[k];
--m_next;
ULONGLONG Cost = m_data_p[m_next]->Cost();
if(Cost < lpD3DTexI->Cost())
{
while(k > 1)
{
if(Cost < m_data_p[parent(k)]->Cost())
{
m_data_p[k] = m_data_p[parent(k)];
m_data_p[k]->m_dwHeapIndex = k;
}
else
break;
k = parent(k);
}
m_data_p[k] = m_data_p[m_next];
m_data_p[k]->m_dwHeapIndex = k;
}
else
{
m_data_p[k] = m_data_p[m_next];
m_data_p[k]->m_dwHeapIndex = k;
heapify(k);
}
lpD3DTexI->m_dwHeapIndex = 0;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::update"
void TextureHeap::update(DWORD k, BOOL inuse, DWORD priority, DWORD ticks)
{
LPDIRECT3DTEXTUREI lpD3DTexI = m_data_p[k];
ULONGLONG Cost;
#ifdef _X86_
_asm
{
mov edx, inuse;
shl edx, 31;
mov eax, priority;
mov ecx, eax;
shr eax, 1;
or edx, eax;
mov DWORD PTR Cost + 4, edx;
shl ecx, 31;
mov eax, ticks;
shr eax, 1;
or eax, ecx;
mov DWORD PTR Cost, eax;
}
#else
Cost = ((ULONGLONG)inuse << 63) + ((ULONGLONG)priority << 31) + ((ULONGLONG)(ticks >> 1));
#endif
if(Cost < lpD3DTexI->Cost())
{
while(k > 1)
{
if(Cost < m_data_p[parent(k)]->Cost())
{
m_data_p[k] = m_data_p[parent(k)];
m_data_p[k]->m_dwHeapIndex = k;
}
else
break;
k = parent(k);
}
lpD3DTexI->m_bInUse = inuse;
lpD3DTexI->m_dwPriority = priority;
lpD3DTexI->m_dwTicks = ticks;
lpD3DTexI->m_dwHeapIndex = k;
m_data_p[k] = lpD3DTexI;
}
else
{
lpD3DTexI->m_bInUse = inuse;
lpD3DTexI->m_dwPriority = priority;
lpD3DTexI->m_dwTicks = ticks;
heapify(k);
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureHeap::resetAllTimeStamps"
void TextureHeap::resetAllTimeStamps(DWORD ticks)
{
for(unsigned i = 1; i < m_next; ++i)
{
update(i, m_data_p[i]->m_bInUse, m_data_p[i]->m_dwPriority, ticks);
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::TextureCacheManager"
TextureCacheManager::TextureCacheManager(LPDIRECT3DI lpD3DI)
{
tcm_ticks = 0;
m_dwScene = 0;
m_dwNumHeaps = 0;
lpDirect3DI=lpD3DI;
#if COLLECTSTATS
m_stats.dwWorkingSet = 0;
m_stats.dwWorkingSetBytes = 0;
m_stats.dwTotalManaged = 0;
m_stats.dwTotalBytes = 0;
m_stats.dwLastPri = 0;
#endif
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::~TextureCacheManager"
TextureCacheManager::~TextureCacheManager()
{
EvictTextures();
delete[] m_heap_p;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::Initialize"
HRESULT TextureCacheManager::Initialize()
{
DDASSERT(((LPDDRAWI_DIRECTDRAW_INT)(lpDirect3DI->lpDD7))->lpLcl);
LPD3DHAL_GLOBALDRIVERDATA lpD3DHALGlobalDriverData = ((LPDDRAWI_DIRECTDRAW_INT)(lpDirect3DI->lpDD7))->lpLcl->lpGbl->lpD3DGlobalDriverData;
if(lpD3DHALGlobalDriverData != NULL)
if(lpD3DHALGlobalDriverData->hwCaps.dwDevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES)
{
m_dwNumHeaps = ((LPDDRAWI_DIRECTDRAW_INT)(lpDirect3DI->lpDD7))->lpLcl->lpGbl->lpD3DExtendedCaps->wMaxSimultaneousTextures;
DDASSERT(m_dwNumHeaps);
if(m_dwNumHeaps < 1)
{
D3D_ERR("Max simultaneous textures not set. Forced to 1.");
m_dwNumHeaps = 1;
}
D3D_INFO(2, "Number of heaps set to %u.", m_dwNumHeaps);
}
else
m_dwNumHeaps = 1;
else
m_dwNumHeaps = 1;
m_heap_p = new TextureHeap[m_dwNumHeaps];
if(m_heap_p == 0)
{
D3D_ERR("Out of memory allocating texture heap.");
return E_OUTOFMEMORY;
}
for(DWORD i = 0; i < m_dwNumHeaps; ++i)
{
if(m_heap_p[i].Initialize() == FALSE)
return E_OUTOFMEMORY;
}
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::FreeTextures"
BOOL TextureCacheManager::FreeTextures(DWORD dwStage, DWORD dwBytes)
{
if(m_heap_p[dwStage].length() == 0)
return false;
LPDIRECT3DTEXTUREI rc;
for(unsigned i = 0; m_heap_p[dwStage].length() != 0 && i < dwBytes; i += rc->m_dwVidBytes)
{
// Find the LRU texture and remove it.
rc = m_heap_p[dwStage].minCost();
if(rc->m_bInUse)
return false;
if(rc->m_dwScene == m_dwScene)
{
if(((LPDDRAWI_DIRECTDRAW_INT)(lpDirect3DI->lpDD7))->lpLcl->lpGbl->lpD3DGlobalDriverData->hwCaps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR)
{
D3D_WARN(0, "Trying to locate texture not used in current scene...");
rc = m_heap_p[dwStage].extractNotInScene(m_dwScene);
if(rc == NULL)
{
D3D_ERR("No such texture found. Cannot evict textures used in current scene.");
return false;
}
D3D_WARN(0, "Texture found!");
remove(rc);
#if COLLECTSTATS
m_stats.dwLastPri = rc->m_dwPriority;
++m_stats.dwNumEvicts;
#endif
}
else
{
D3D_WARN(1, "Texture cache thrashing. Removing MRU texture.");
rc = m_heap_p[dwStage].extractMax();
if(rc == NULL)
{
D3D_ERR("All textures in use, cannot evict texture.");
return false;
}
remove(rc);
#if COLLECTSTATS
m_stats.bThrashing = TRUE;
m_stats.dwLastPri = rc->m_dwPriority;
++m_stats.dwNumEvicts;
#endif
}
}
else
{
rc = m_heap_p[dwStage].extractMin();
remove(rc);
#if COLLECTSTATS
m_stats.dwLastPri = rc->m_dwPriority;
++m_stats.dwNumEvicts;
#endif
}
D3D_INFO(2, "Removed texture with timestamp %u,%u (current = %u).", rc->m_dwPriority, rc->m_dwTicks, tcm_ticks);
}
return true;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::allocNode"
HRESULT TextureCacheManager::allocNode(LPDIRECT3DTEXTUREI lpD3DTexI, LPDIRECT3DDEVICEI lpDevI)
{
HRESULT ddrval;
DWORD trycount = 0, bytecount = lpD3DTexI->m_dwBytes;
// We need to make sure that we don't evict any mapped textures
for(DWORD dwStage = 0; dwStage < lpDevI->dwMaxTextureBlendStages; ++dwStage)
{
if(lpDevI->lpD3DMappedTexI[dwStage])
{
lpDevI->lpD3DMappedTexI[dwStage]->m_bInUse = TRUE;
UpdatePriority(lpDevI->lpD3DMappedTexI[dwStage]);
}
}
// Attempt to allocate a texture.
do
{
++trycount;
DDASSERT(lpD3DTexI->lpDDS == NULL);
ddrval = lpDirect3DI->lpDD7->CreateSurface(&lpD3DTexI->ddsd, &lpD3DTexI->lpDDS, NULL);
if (DD_OK == ddrval) // No problem, there is enough memory.
{
static_cast<DIRECT3DTEXTURED3DM*>(lpD3DTexI)->MarkDirtyPointers();
lpD3DTexI->m_dwScene = m_dwScene;
lpD3DTexI->m_dwTicks = tcm_ticks;
DDASSERT(lpD3DTexI->m_dwHeapIndex == 0);
if(!m_heap_p[lpD3DTexI->ddsd.dwTextureStage].add(lpD3DTexI))
{
ddrval = DDERR_OUTOFMEMORY;
goto exit2;
}
#if COLLECTSTATS
++m_stats.dwWorkingSet;
m_stats.dwWorkingSetBytes += lpD3DTexI->m_dwVidBytes;
++m_stats.dwNumVidCreates;
#endif
}
else if(ddrval == DDERR_OUTOFVIDEOMEMORY) // If out of video memory
{
if (!FreeTextures(lpD3DTexI->ddsd.dwTextureStage, bytecount))
{
D3D_ERR("all Freed no further video memory available");
ddrval = DDERR_OUTOFVIDEOMEMORY; //nothing left
goto exit1;
}
bytecount <<= 1;
}
else
{
D3D_ERR("Unexpected error got in allocNode %08x", ddrval);
goto exit1;
}
}
while(ddrval == DDERR_OUTOFVIDEOMEMORY);
if(trycount > 1)
{
D3DTextureUpdate(static_cast<LPUNKNOWN>(&(lpDirect3DI->mD3DUnk)));
D3D_WARN(1, "Allocated texture after %u tries.", trycount);
}
if (lpD3DTexI->ddsd.ddpfPixelFormat.dwFlags & (DDPF_PALETTEINDEXED8 | DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED2 | DDPF_PALETTEINDEXED1))
{
LPDIRECTDRAWPALETTE lpDDPal;
if (DD_OK != (ddrval = lpD3DTexI->lpDDSSys->GetPalette(&lpDDPal)))
{
D3D_ERR("failed to check for palette on texture");
goto exit3;
}
if (DD_OK != (ddrval = lpD3DTexI->lpDDS->SetPalette(lpDDPal)))
{
lpDDPal->Release();
D3D_ERR("SetPalette returned error");
goto exit3;
}
lpDDPal->Release();
}
{ // scope for CLockD3DST
// Mark everything dirty before copying sysmem to vidmem
// else CopySurface will not copy anything
lpD3DTexI->bDirty = TRUE;
CLockD3DST lockObject(lpDevI, DPF_MODNAME, REMIND("")); // we access DDraw gbl in CopySurface
// 0xFFFFFFFF is equivalent to ALL_FACES, but in addition indicates to CopySurface
// that this is a sysmem -> vidmem transfer.
if (DD_OK != (ddrval = lpDevI->CopySurface(lpD3DTexI->lpDDS, NULL, lpD3DTexI->lpDDSSys, NULL, 0xFFFFFFFF)))
{
D3D_ERR("CopySurface returned error");
goto exit3;
}
lpD3DTexI->bDirty = FALSE;
}
lpD3DTexI->DDS1Tex.lpLcl = ((LPDDRAWI_DDRAWSURFACE_INT)(lpD3DTexI->lpDDS))->lpLcl;
lpD3DTexI->m_hTex = ((LPDDRAWI_DDRAWSURFACE_INT)lpD3DTexI->lpDDS)->lpLcl->lpSurfMore->dwSurfaceHandle;
ddrval = D3D_OK;
goto exit1;
exit3:
m_heap_p[lpD3DTexI->ddsd.dwTextureStage].del(lpD3DTexI->m_dwHeapIndex);
exit2:
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS = NULL;
exit1:
for(dwStage = 0; dwStage < lpDevI->dwMaxTextureBlendStages; ++dwStage)
{
if(lpDevI->lpD3DMappedTexI[dwStage])
{
lpDevI->lpD3DMappedTexI[dwStage]->m_bInUse = FALSE;
UpdatePriority(lpDevI->lpD3DMappedTexI[dwStage]);
}
}
return ddrval;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::remove"
//remove all HW handles and release surface
void TextureCacheManager::remove(LPDIRECT3DTEXTUREI lpD3DTexI)
{
LPD3DI_TEXTUREBLOCK tBlock = LIST_FIRST(&lpD3DTexI->blocks);
while(tBlock)
{
CLockD3DST lockObject(tBlock->lpDevI, DPF_MODNAME, REMIND(""));
D3DI_RemoveTextureHandle(tBlock);
tBlock=LIST_NEXT(tBlock,list);
}
D3D_INFO(7,"removing lpD3DTexI=%08lx lpDDS=%08lx",lpD3DTexI,lpD3DTexI->lpDDS);
lpD3DTexI->lpDDS->Release();
lpD3DTexI->lpDDS = NULL;
lpD3DTexI->m_hTex = 0;
#if COLLECTSTATS
--m_stats.dwWorkingSet;
m_stats.dwWorkingSetBytes -= lpD3DTexI->m_dwVidBytes;
#endif
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::EvictTextures"
void TextureCacheManager::EvictTextures()
{
for(DWORD i = 0; i < m_dwNumHeaps; ++i)
while(m_heap_p[i].length())
{
LPDIRECT3DTEXTUREI lpD3DTexI = m_heap_p[i].extractMin();
remove(lpD3DTexI);
}
D3DTextureUpdate(static_cast<LPUNKNOWN>(&(lpDirect3DI->mD3DUnk)));
tcm_ticks = 0;
m_dwScene = 0;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::CheckIfLost"
BOOL TextureCacheManager::CheckIfLost()
{
for(DWORD i = 0; i < m_dwNumHeaps; ++i)
{
if(m_heap_p[i].length())
{
if(((LPDDRAWI_DDRAWSURFACE_INT)(m_heap_p[i].minCost()->lpDDS))->lpLcl->dwFlags & DDRAWISURF_INVALID)
return TRUE;
else
return FALSE;
}
}
return FALSE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "TextureCacheManager::TimeStamp"
void TextureCacheManager::TimeStamp(LPDIRECT3DTEXTUREI lpD3DTexI)
{
lpD3DTexI->m_dwScene = m_dwScene;
m_heap_p[lpD3DTexI->ddsd.dwTextureStage].update(lpD3DTexI->m_dwHeapIndex, lpD3DTexI->m_bInUse, lpD3DTexI->m_dwPriority, tcm_ticks);
unsigned tickp2 = tcm_ticks + 2;
if(tickp2 > tcm_ticks)
{
tcm_ticks = tickp2;
}
else // counter has overflowed. Let's reset all timestamps to zero
{
D3D_INFO(2, "Timestamp counter overflowed. Reseting timestamps for all textures.");
tcm_ticks = 0;
for(DWORD i = 0; i < m_dwNumHeaps; ++i)
m_heap_p[i].resetAllTimeStamps(0);
}
}