Code Search for Developers
 
 
  

GSTextureCache.cpp from guliverkli at Krugle


Show GSTextureCache.cpp syntax highlighted

/* 
 *	Copyright (C) 2003-2005 Gabest
 *	http://www.gabest.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "StdAfx.h"
#include "GSTextureCache.h"
#include "GSHash.h"
#include "GSRendererHW.h"

//

bool IsRenderTarget(IDirect3DTexture9* pTexture)
{
	D3DSURFACE_DESC desc;
	memset(&desc, 0, sizeof(desc));
	return pTexture && S_OK == pTexture->GetLevelDesc(0, &desc) && (desc.Usage&D3DUSAGE_RENDERTARGET);
}

bool HasSharedBits(DWORD sbp, DWORD spsm, DWORD dbp, DWORD dpsm)
{
	if(sbp != dbp) return false;

	switch(spsm)
	{
	case PSM_PSMCT32:
	case PSM_PSMCT16:
	case PSM_PSMCT16S:
	case PSM_PSMT8:
	case PSM_PSMT4:
		return true;
	case PSM_PSMCT24:
		return !(dpsm == PSM_PSMT8H || dpsm == PSM_PSMT4HL || dpsm == PSM_PSMT4HH);
	case PSM_PSMT8H:
		return !(dpsm == PSM_PSMCT24);
	case PSM_PSMT4HL:
		return !(dpsm == PSM_PSMCT24 || dpsm == PSM_PSMT4HH);
	case PSM_PSMT4HH:
		return !(dpsm == PSM_PSMCT24 || dpsm == PSM_PSMT4HL);
	}

	return true;
}

//

GSDirtyRect::GSDirtyRect(DWORD PSM, CRect r)
{
	m_PSM = PSM;
	m_rcDirty = r;
}

CRect GSDirtyRect::GetDirtyRect(const GIFRegTEX0& TEX0)
{
	CRect rcDirty = m_rcDirty;

	CSize src = GSLocalMemory::m_psmtbl[m_PSM].bs;
	rcDirty.left = (rcDirty.left) & ~(src.cx-1);
	rcDirty.right = (rcDirty.right + (src.cx-1) /* + 1 */) & ~(src.cx-1);
	rcDirty.top = (rcDirty.top) & ~(src.cy-1);
	rcDirty.bottom = (rcDirty.bottom + (src.cy-1) /* + 1 */) & ~(src.cy-1);

	if(m_PSM != TEX0.PSM)
	{
		CSize dst = GSLocalMemory::m_psmtbl[TEX0.PSM].bs;
		rcDirty.left = MulDiv(m_rcDirty.left, dst.cx, src.cx);
		rcDirty.right = MulDiv(m_rcDirty.right, dst.cx, src.cx);
		rcDirty.top = MulDiv(m_rcDirty.top, dst.cy, src.cy);
		rcDirty.bottom = MulDiv(m_rcDirty.bottom, dst.cy, src.cy);
	}

	rcDirty &= CRect(0, 0, 1<<TEX0.TW, 1<<TEX0.TH);

	return rcDirty;
}

void GSDirtyRectList::operator = (const GSDirtyRectList& l)
{
	RemoveAll();
	POSITION pos = l.GetHeadPosition();
	while(pos) AddTail(l.GetNext(pos));
}

CRect GSDirtyRectList::GetDirtyRect(const GIFRegTEX0& TEX0)
{
	if(IsEmpty()) return CRect(0, 0, 0, 0);
	CRect r(INT_MAX, INT_MAX, 0, 0);
	POSITION pos = GetHeadPosition();
	while(pos) r |= GetNext(pos).GetDirtyRect(TEX0);
	return r;
}

//

GSTextureBase::GSTextureBase()
{
	m_scale = scale_t(1, 1);
	m_fRT = false;
	memset(&m_desc, 0, sizeof(m_desc));
}

GSTexture::GSTexture()
{
	m_TEX0.TBP0 = ~0;
	m_rcValid = CRect(0, 0, 0, 0);
	m_dwHash = ~0;
	m_nHashDiff = m_nHashSame = 0;
	m_rcHash = CRect(0, 0, 0, 0);
	m_nBytes = 0;
	m_nAge = 0;
	m_nVsyncs = 0;
	m_fTemp = false;
}

//

GSTextureCache::GSTextureCache()
{
}

GSTextureCache::~GSTextureCache()
{
	RemoveAll();
}

HRESULT GSTextureCache::CreateTexture(GSState* s, GSTexture* pt, DWORD PSM, DWORD CPSM)
{
	if(!pt || pt->m_pTexture) {ASSERT(0); return E_FAIL;}

	int w = 1 << pt->m_TEX0.TW;
	int h = 1 << pt->m_TEX0.TH;

	int bpp = 0;
	D3DFORMAT fmt = D3DFMT_UNKNOWN;
	D3DFORMAT palfmt = D3DFMT_UNKNOWN;

	switch(PSM)
	{
	default:
	case PSM_PSMCT32:
		bpp = 32;
		fmt = D3DFMT_A8R8G8B8;
		break;
	case PSM_PSMCT24:
		bpp = 32;
		fmt = D3DFMT_X8R8G8B8;
		break;
	case PSM_PSMCT16:
	case PSM_PSMCT16S:
		bpp = 16;
		fmt = D3DFMT_A1R5G5B5;
		break;
	case PSM_PSMT8:
	case PSM_PSMT4:
	case PSM_PSMT8H:
	case PSM_PSMT4HL:
	case PSM_PSMT4HH:
		bpp = 8;
		fmt = D3DFMT_L8;
		palfmt = CPSM == PSM_PSMCT32 ? D3DFMT_A8R8G8B8 : D3DFMT_A1R5G5B5;
		break;
	}

	pt->m_nBytes = w*h*bpp>>3;

	POSITION pos = m_pTexturePool.GetHeadPosition();
	while(pos)
	{
		IDirect3DTexture9* pTexture = m_pTexturePool.GetNext(pos);

		D3DSURFACE_DESC desc;
		memset(&desc, 0, sizeof(desc));
		pTexture->GetLevelDesc(0, &desc);

		if(w == desc.Width && h == desc.Height && fmt == desc.Format && !IsTextureInCache(pTexture))
		{
			pt->m_pTexture = pTexture;
			pt->m_desc = desc;
			break;
		}
	}

	if(!pt->m_pTexture)
	{
		while(m_pTexturePool.GetCount() > 20)
			m_pTexturePool.RemoveTail();

		if(FAILED(s->m_pD3DDev->CreateTexture(w, h, 1, 0, fmt, D3DPOOL_MANAGED, &pt->m_pTexture, NULL)))
			return E_FAIL;

		pt->m_pTexture->GetLevelDesc(0, &pt->m_desc);

		m_pTexturePool.AddHead(pt->m_pTexture);
	}

	if(bpp == 8)
	{
		if(FAILED(s->m_pD3DDev->CreateTexture(256, 1, 1, 0, palfmt, D3DPOOL_MANAGED, &pt->m_pPalette, NULL)))
		{
			pt->m_pTexture = NULL;
			return E_FAIL;
		}
	}

	return S_OK;
}

bool GSTextureCache::IsTextureInCache(IDirect3DTexture9* pTexture)
{
	POSITION pos = GetHeadPosition();
	while(pos)
	{
		if(GetNext(pos)->m_pTexture == pTexture)
			return true;
	}

	return false;
}

void GSTextureCache::RemoveOldTextures(GSState* s)
{
	DWORD nBytes = 0;

	POSITION pos = GetHeadPosition();
	while(pos) nBytes += GetNext(pos)->m_nBytes;

	pos = GetTailPosition();
	while(pos && nBytes > 96*1024*1024/*s->m_ddcaps.dwVidMemTotal*/)
	{
#ifdef DEBUG_LOG
		s->LOG(_T("*TC2 too many textures in cache (%d, %.2f MB)\n"), GetCount(), 1.0f*nBytes/1024/1024);
#endif
		POSITION cur = pos;

		GSTexture* pt = GetPrev(pos);
		if(!pt->m_fRT)
		{
			nBytes -= pt->m_nBytes;
			RemoveAt(cur);
			delete pt;
		}
	}
}

static bool RectInRect(const RECT& inner, const RECT& outer)
{
	return outer.left <= inner.left && inner.right <= outer.right
		&& outer.top <= inner.top && inner.bottom <= outer.bottom;
}

static bool RectInRectH(const RECT& inner, const RECT& outer)
{
	return outer.top <= inner.top && inner.bottom <= outer.bottom;
}

static bool RectInRectV(const RECT& inner, const RECT& outer)
{
	return outer.left <= inner.left && inner.right <= outer.right;
}

bool GSTextureCache::GetDirtyRect(GSState* s, GSTexture* pt, CRect& r)
{
	int w = 1 << pt->m_TEX0.TW;
	int h = 1 << pt->m_TEX0.TH;

	r.SetRect(0, 0, w, h);

// FIXME: kyo's left hand after being selected for player one (PS2-SNK_Vs_Capcom_SVC_Chaos_PAL_CDFull.iso)
// return true;

	s->MinMaxUV(w, h, r);

	CRect rcDirty = pt->m_rcDirty.GetDirtyRect(pt->m_TEX0);
	CRect rcValid = pt->m_rcValid;

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 used %d,%d-%d,%d (%dx%d), valid %d,%d-%d,%d, dirty %d,%d-%d,%d\n"), r, w, h, rcValid, rcDirty);
#endif

	if(RectInRect(r, rcValid))
	{
		if(rcDirty.IsRectEmpty()) return false;
		else if(RectInRect(rcDirty, r)) r = rcDirty;
		else if(RectInRect(rcDirty, rcValid)) r |= rcDirty;
		else r = rcValid | rcDirty;
	}
	else
	{
		if(RectInRectH(r, rcValid) && (r.left >= rcValid.left || r.right <= rcValid.right))
		{
			r.top = rcValid.top;
			r.bottom = rcValid.bottom;
			if(r.left < rcValid.left) r.right = rcValid.left;
			else /*if(r.right > rcValid.right)*/ r.left = rcValid.right;
		}
		else if(RectInRectV(r, rcValid) && (r.top >= rcValid.top || r.bottom <= rcValid.bottom))
		{
			r.left = rcValid.left;
			r.right = rcValid.right;
			if(r.top < rcValid.top) r.bottom = rcValid.top;
			else /*if(r.bottom > rcValid.bottom)*/ r.top = rcValid.bottom;
		}
		else
		{
			r |= rcValid;
		}
	}

	return true;
}

DWORD GSTextureCache::HashTexture(const CRect& r, int pitch, void* bits)
{
	// TODO: make the hash more unique

	BYTE* p = (BYTE*)bits;
	DWORD hash = r.left + r.right + r.top + r.bottom + pitch + *(BYTE*)bits;

	if(r.Width() > 0)
	{
		int size = r.Width()*r.Height();
/*
		if(size <= 8*8) return rand(); // :P
		else 
*/
		if(size <= 16*16) hash += hash_crc(r, pitch, p);
		else if(size <= 32*32) hash += hash_adler(r, pitch, p);
		else hash += hash_checksum(r, pitch, p);
	}

	return hash;
}

HRESULT GSTextureCache::UpdateTexture(GSState* s, GSTexture* pt, GSLocalMemory::readTexture rt)
{
	CRect r;
	if(!GetDirtyRect(s, pt, r))
		return S_OK;

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 updating texture %d,%d-%d,%d (%dx%d)\n"), r.left, r.top, r.right, r.bottom, 1 << pt->m_TEX0.TW, 1 << pt->m_TEX0.TH);
#endif

	int bpp = 0;

	switch(pt->m_desc.Format)
	{
	case D3DFMT_A8R8G8B8: bpp = 32; break;
	case D3DFMT_X8R8G8B8: bpp = 32; break;
	case D3DFMT_A1R5G5B5: bpp = 16; break;
	case D3DFMT_L8: bpp = 8; break;
	default: ASSERT(0); return E_FAIL;
	}

	D3DLOCKED_RECT lr;
	if(FAILED(pt->m_pTexture->LockRect(0, &lr, &r, D3DLOCK_NO_DIRTY_UPDATE))) {ASSERT(0); return E_FAIL;}
	(s->m_lm.*rt)(r, (BYTE*)lr.pBits, lr.Pitch, s->m_ctxt->TEX0, s->m_de.TEXA, s->m_ctxt->CLAMP);
	s->m_perfmon.IncCounter(GSPerfMon::c_unswizzle, r.Width()*r.Height()*bpp>>3);
	pt->m_pTexture->UnlockRect(0);

	pt->m_rcValid |= r;
	pt->m_rcDirty.RemoveAll();

	const static DWORD limit = 7;

	if((pt->m_nHashDiff & limit) && pt->m_nHashDiff >= limit && pt->m_rcHash == pt->m_rcValid) // predicted to be dirty
	{
		pt->m_nHashDiff++;
	}
	else
	{
		if(FAILED(pt->m_pTexture->LockRect(0, &lr, &pt->m_rcValid, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_READONLY))) {ASSERT(0); return E_FAIL;}
		DWORD dwHash = HashTexture(
			CRect((pt->m_rcValid.left>>2)*(bpp>>3), pt->m_rcValid.top, (pt->m_rcValid.right>>2)*(bpp>>3), pt->m_rcValid.bottom), 
			lr.Pitch, lr.pBits);
		pt->m_pTexture->UnlockRect(0);

		if(pt->m_rcHash != pt->m_rcValid)
		{
			pt->m_nHashDiff = 0;
			pt->m_nHashSame = 0;
			pt->m_rcHash = pt->m_rcValid;
			pt->m_dwHash = dwHash;
		}
		else
		{
			if(pt->m_dwHash != dwHash)
			{
				pt->m_nHashDiff++;
				pt->m_nHashSame = 0;
				pt->m_dwHash = dwHash;
			}
			else
			{
				if(pt->m_nHashDiff < limit) r.SetRect(0, 0, 1, 1);
				// else pt->m_dwHash is not reliable, must update
				pt->m_nHashDiff = 0;
				pt->m_nHashSame++;
			}
		}
	}

	pt->m_pTexture->AddDirtyRect(&r);
	pt->m_pTexture->PreLoad();
	s->m_perfmon.IncCounter(GSPerfMon::c_texture, r.Width()*r.Height()*bpp>>3);

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 texture was updated, valid %d,%dx%d,%d\n"), pt->m_rcValid);
#endif

#ifdef DEBUG_SAVETEXTURES   
if(s->m_ctxt->FRAME.Block() == 0x00000 && pt->m_TEX0.TBP0 == 0x02800)
{   
	CString fn;   
	fn.Format(_T("c:\\%08I64x_%I64d_%I64d_%I64d_%I64d_%I64d_%I64d_%I64d-%I64d_%I64d-%I64d.bmp"),   
			pt->m_TEX0.TBP0, pt->m_TEX0.PSM, pt->m_TEX0.TBW,   
			pt->m_TEX0.TW, pt->m_TEX0.TH,   
			pt->m_CLAMP.WMS, pt->m_CLAMP.WMT, pt->m_CLAMP.MINU, pt->m_CLAMP.MAXU, pt->m_CLAMP.MINV, pt->m_CLAMP.MAXV);   
	D3DXSaveTextureToFile(fn, D3DXIFF_BMP, pt->m_pTexture, NULL);   
}   
#endif 

	return S_OK;
}

GSTexture* GSTextureCache::ConvertRTPitch(GSState* s, GSTexture* pt)
{
	if(pt->m_TEX0.TBW == s->m_ctxt->TEX0.TBW)
		return pt;

	// sfex3 uses this trick (bw: 10 -> 5, wraps the right side below the left)
	ASSERT(pt->m_TEX0.TBW > s->m_ctxt->TEX0.TBW); // otherwise scale.x need to be reduced to make the larger texture fit (TODO)

	int bw = 64;
	int bh = s->m_ctxt->TEX0.PSM == PSM_PSMCT32 || s->m_ctxt->TEX0.PSM == PSM_PSMCT24 ? 32 : 64;

	int sw = pt->m_TEX0.TBW << 6;

	int dw = s->m_ctxt->TEX0.TBW << 6;
	int dh = 1 << s->m_ctxt->TEX0.TH;

	// TRACE(_T("ConvertRT: %05x %x %d -> %d\n"), (DWORD)s->m_ctxt->TEX0.TBP0, (DWORD)s->m_ctxt->TEX0.PSM, (DWORD)pt->m_TEX0.TBW, (DWORD)s->m_ctxt->TEX0.TBW);

	HRESULT hr;
/*
if(s->m_perfmon.GetFrame() > 400)
hr = D3DXSaveTextureToFile(_T("g:/1.bmp"), D3DXIFF_BMP, pt->m_pTexture, NULL);
*/
	D3DSURFACE_DESC desc;
	hr = pt->m_pTexture->GetLevelDesc(0, &desc);
	if(FAILED(hr)) return NULL;

	CComPtr<IDirect3DTexture9> pRT;
	if(FAILED(hr = CreateRT(s, desc.Width, desc.Height, &pRT)))
		return NULL;

	CComPtr<IDirect3DSurface9> pSrc, pDst;
	hr = pRT->GetSurfaceLevel(0, &pSrc);
	if(FAILED(hr)) return NULL;
	hr = pt->m_pTexture->GetSurfaceLevel(0, &pDst);
	if(FAILED(hr)) return NULL;

	hr = s->m_pD3DDev->StretchRect(pDst, NULL, pSrc, NULL, D3DTEXF_POINT);
	if(FAILED(hr)) return NULL;

	scale_t scale(pt->m_pTexture);

	for(int dy = 0; dy < dh; dy += bh)
	{
		for(int dx = 0; dx < dw; dx += bw)
		{
			int o = dy * dw / bh + dx;

			int sx = o % sw;
			int sy = o / sw;

			// TRACE(_T("%d,%d - %d,%d  <=  %d,%d - %d,%d\n"), dx, dy, dx + bw, dy + bh, sx, sy, sx + bw, sy + bh);

			CRect src, dst;

			src.left = (LONG)(scale.x * sx + 0.5f);
			src.top = (LONG)(scale.y * sy + 0.5f);
			src.right = (LONG)(scale.x * (sx + bw) + 0.5f);
			src.bottom = (LONG)(scale.y * (sy + bh) + 0.5f);

			dst.left = (LONG)(scale.x * dx + 0.5f);
			dst.top = (LONG)(scale.y * dy + 0.5f);
			dst.right = (LONG)(scale.x * (dx + bw) + 0.5f);
			dst.bottom = (LONG)(scale.y * (dy + bh) + 0.5f);

			hr = s->m_pD3DDev->StretchRect(pSrc, src, pDst, dst, D3DTEXF_POINT);

			// TODO: this is quite a lot of StretchRect call, do it with one DrawPrimUP
		}
	}

	pt->m_TEX0.TW = s->m_ctxt->TEX0.TW;
	pt->m_TEX0.TBW = s->m_ctxt->TEX0.TBW;
/*		
if(s->m_perfmon.GetFrame() > 400)
hr = D3DXSaveTextureToFile(_T("g:/2.bmp"), D3DXIFF_BMP, pt->m_pTexture, NULL);
*/

	return pt;
}

GSTexture* GSTextureCache::ConvertRTWidthHeight(GSState* s, GSTexture* pt)
{
	int tw = pt->m_scale.x * (1 << s->m_ctxt->TEX0.TW);
	int th = pt->m_scale.y * (1 << s->m_ctxt->TEX0.TH);

	int rw = pt->m_desc.Width;
	int rh = pt->m_desc.Height;

	if(tw != rw || th != rh)
	//if(tw < rw && th <= rh || tw <= rw && th < rh)
	{
		GSTexture* pt2 = new GSTexture();

		pt2->m_pPalette = pt->m_pPalette;
		pt2->m_fRT = pt->m_pPalette == NULL;
		pt2->m_scale = pt->m_scale;
		pt2->m_fTemp = true;

		POSITION pos = pt->m_pSubTextures.GetHeadPosition();
		while(pos)
		{
			IDirect3DTexture9* pTexture = pt->m_pSubTextures.GetNext(pos);
			pTexture->GetLevelDesc(0, &pt2->m_desc);
			scale_t scale(pTexture);
			if(pt2->m_desc.Width == tw && pt2->m_desc.Height == th
			&& pt2->m_scale.x == scale.x && pt2->m_scale.y == scale.y)
			{
				pt2->m_pTexture = pTexture;
				break;
			}
		}

		if(!pt2->m_pTexture)
		{
			CRect dst(0, 0, tw, th);
			
			if(tw > rw)
			{
				pt2->m_scale.x = pt2->m_scale.x * rw / tw;
				dst.right = rw * rw / tw;
				tw = rw;
			}
			
			if(th > rh)
			{
				pt2->m_scale.y = pt2->m_scale.y * rh / th;
				dst.bottom = rh * rh / th;
				th = rh;
			}

			CRect src(0, 0, tw, th);

			HRESULT hr;

			if(FAILED(hr = CreateRT(s, tw, th, &pt2->m_pTexture)) || FAILED(hr = pt2->m_pTexture->GetLevelDesc(0, &pt2->m_desc)))
			{
				delete pt2; 
				return false;
			}

			CComPtr<IDirect3DSurface9> pSrc, pDst;
			hr = pt->m_pTexture->GetSurfaceLevel(0, &pSrc);
			hr = pt2->m_pTexture->GetSurfaceLevel(0, &pDst);

			ASSERT(pSrc);
			ASSERT(pDst);

			hr = s->m_pD3DDev->StretchRect(pSrc, src, pDst, dst, src == dst ? D3DTEXF_POINT : D3DTEXF_LINEAR);

			pt2->m_scale.Set(pt2->m_pTexture);
			pt->m_pSubTextures.AddTail(pt2->m_pTexture);
		}
		
		pt = pt2;
	}

	return pt;
}

HRESULT GSTextureCache::CreateRT(GSState* s, int w, int h, IDirect3DTexture9** ppRT)
{
	ASSERT(ppRT && *ppRT == NULL);

	HRESULT hr;

	POSITION pos = m_pRTPool.GetHeadPosition();
	while(pos)
	{
		IDirect3DTexture9* pRT = m_pRTPool.GetNext(pos);
		D3DSURFACE_DESC desc;
		pRT->GetLevelDesc(0, &desc);
		if(desc.Width == w && desc.Height == h)
		{
			(*ppRT = pRT)->AddRef();
			return S_OK;
		}
	}

	hr = s->m_pD3DDev->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, ppRT, NULL);
	if(FAILED(hr)) return hr;
/**/
	m_pRTPool.AddHead(*ppRT);
	while(m_pRTPool.GetCount() > 3) m_pRTPool.RemoveTail();

	return S_OK;
}

GSTexture* GSTextureCache::ConvertRT(GSState* s, GSTexture* pt)
{
	ASSERT(pt->m_fRT);

	// FIXME: RT + 8h,4hl,4hh

	if(s->m_ctxt->TEX0.PSM == PSM_PSMT8H)
	{
		if(!pt->m_pPalette)
		{
			if(FAILED(s->m_pD3DDev->CreateTexture(256, 1, 1, 0, s->m_ctxt->TEX0.CPSM == PSM_PSMCT32 ? D3DFMT_A8R8G8B8 : D3DFMT_A1R5G5B5, D3DPOOL_MANAGED, &pt->m_pPalette, NULL)))
				return NULL;
		}
	}
	else if(GSLocalMemory::m_psmtbl[s->m_ctxt->TEX0.PSM].pal)
	{
		return NULL;
	}

	pt = ConvertRTPitch(s, pt);

	pt = ConvertRTWidthHeight(s, pt);

	return pt;
}

bool GSTextureCache::Fetch(GSState* s, GSTextureBase& t)
{
	GSTexture* pt = NULL;

	int nPaletteEntries = GSLocalMemory::m_psmtbl[s->m_ctxt->TEX0.PSM].pal;

	DWORD clut[256];

	if(nPaletteEntries)
	{
		s->m_lm.SetupCLUT32(s->m_ctxt->TEX0, s->m_de.TEXA);
		s->m_lm.CopyCLUT32(clut, nPaletteEntries);
	}

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 Fetch %dx%d %05I64x %I64d (%d)\n"), 
		1 << s->m_ctxt->TEX0.TW, 1 << s->m_ctxt->TEX0.TH, 
		s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM, nPaletteEntries);
#endif

	enum lookupresult {notfound, needsupdate, found} lr = notfound;

	POSITION pos = GetHeadPosition();
	while(pos && !pt)
	{
		POSITION cur = pos;
		pt = GetNext(pos);

		if(HasSharedBits(pt->m_TEX0.TBP0, pt->m_TEX0.PSM, s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM))
		{
			if(pt->m_fRT)
			{
				lr = found;

				if(!(pt = ConvertRT(s, pt)))
					return false;
			}
			else if(s->m_ctxt->TEX0.PSM == pt->m_TEX0.PSM && pt->m_TEX0.TBW == s->m_ctxt->TEX0.TBW
			&& s->m_ctxt->TEX0.TW == pt->m_TEX0.TW && s->m_ctxt->TEX0.TH == pt->m_TEX0.TH
			&& (!(s->m_ctxt->CLAMP.WMS&2) && !(pt->m_CLAMP.WMS&2) && !(s->m_ctxt->CLAMP.WMT&2) && !(pt->m_CLAMP.WMT&2) || s->m_ctxt->CLAMP.i64 == pt->m_CLAMP.i64)
			&& s->m_de.TEXA.TA0 == pt->m_TEXA.TA0 && s->m_de.TEXA.TA1 == pt->m_TEXA.TA1 && s->m_de.TEXA.AEM == pt->m_TEXA.AEM
			&& (!nPaletteEntries || s->m_ctxt->TEX0.CPSM == pt->m_TEX0.CPSM && !memcmp(pt->m_clut, clut, nPaletteEntries*sizeof(clut[0]))))
			{
				lr = needsupdate;
			}
		}

		if(lr != notfound) {MoveToHead(cur); break;}

		pt = NULL;
	}

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 lr = %s\n"), lr == found ? _T("found") : lr == needsupdate ? _T("needsupdate") : _T("notfound"));
#endif

	if(lr == notfound)
	{
		pt = new GSTexture();

		pt->m_TEX0 = s->m_ctxt->TEX0;
		pt->m_CLAMP = s->m_ctxt->CLAMP;
		pt->m_TEXA = s->m_de.TEXA;

		if(!SUCCEEDED(CreateTexture(s, pt, PSM_PSMCT32)))
		{
			delete pt;
			return false;
		}

		RemoveOldTextures(s);

		AddHead(pt);

		lr = needsupdate;
	}

	ASSERT(pt);

	if(pt && nPaletteEntries)
	{
		memcpy(pt->m_clut, clut, nPaletteEntries*sizeof(clut[0]));
	}

	if(lr == needsupdate)
	{
		UpdateTexture(s, pt, &GSLocalMemory::ReadTexture);

		lr = found;
	}

	if(lr == found)
	{
#ifdef DEBUG_LOG
		s->LOG(_T("*TC2 texture was found, age %d -> 0\n"), pt->m_nAge);
#endif
		pt->m_nAge = 0;
		t = *pt;
		if(pt->m_fTemp) delete pt;
		return true;
	}

	return false;
}

bool GSTextureCache::FetchP(GSState* s, GSTextureBase& t)
{
	GSTexture* pt = NULL;

	int nPaletteEntries = GSLocalMemory::m_psmtbl[s->m_ctxt->TEX0.PSM].pal;

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 Fetch %dx%d %05I64x %I64d (%d)\n"), 
		1 << s->m_ctxt->TEX0.TW, 1 << s->m_ctxt->TEX0.TH, 
		s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM, nPaletteEntries);
#endif

	enum lookupresult {notfound, needsupdate, found} lr = notfound;

	POSITION pos = GetHeadPosition();
	while(pos && !pt)
	{
		POSITION cur = pos;
		pt = GetNext(pos);

		if(HasSharedBits(pt->m_TEX0.TBP0, pt->m_TEX0.PSM, s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM))
		{
			if(pt->m_fRT)
			{
				lr = found;

				if(!(pt = ConvertRT(s, pt)))
					return false;
			}
			else if(s->m_ctxt->TEX0.PSM == pt->m_TEX0.PSM && pt->m_TEX0.TBW == s->m_ctxt->TEX0.TBW
			&& s->m_ctxt->TEX0.TW == pt->m_TEX0.TW && s->m_ctxt->TEX0.TH == pt->m_TEX0.TH
			&& (!(s->m_ctxt->CLAMP.WMS&2) && !(pt->m_CLAMP.WMS&2) && !(s->m_ctxt->CLAMP.WMT&2) && !(pt->m_CLAMP.WMT&2) || s->m_ctxt->CLAMP.i64 == pt->m_CLAMP.i64))
			{
				lr = needsupdate;
			}
		}

		if(lr != notfound) {MoveToHead(cur); break;}
		
		pt = NULL;
	}

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 lr = %s\n"), lr == found ? _T("found") : lr == needsupdate ? _T("needsupdate") : _T("notfound"));
#endif

	if(lr == notfound)
	{
		pt = new GSTexture();

		pt->m_TEX0 = s->m_ctxt->TEX0;
		pt->m_CLAMP = s->m_ctxt->CLAMP;
		// pt->m_TEXA = s->m_de.TEXA;

		if(!SUCCEEDED(CreateTexture(s, pt, s->m_ctxt->TEX0.PSM, PSM_PSMCT32)))
		{
			delete pt;
			return false;
		}

		RemoveOldTextures(s);

		AddHead(pt);

		lr = needsupdate;
	}

	ASSERT(pt);

	if(pt && pt->m_pPalette) 
	{
		D3DLOCKED_RECT r;
		if(FAILED(pt->m_pPalette->LockRect(0, &r, NULL, 0)))
			return false;
		s->m_lm.ReadCLUT32(s->m_ctxt->TEX0, s->m_de.TEXA, (DWORD*)r.pBits);
		pt->m_pPalette->UnlockRect(0);
		s->m_perfmon.IncCounter(GSPerfMon::c_texture, 256*4);
	}

	if(lr == needsupdate)
	{
		UpdateTexture(s, pt, &GSLocalMemory::ReadTextureP);

		lr = found;
	}

	if(lr == found)
	{
#ifdef DEBUG_LOG
		s->LOG(_T("*TC2 texture was found, age %d -> 0\n"), pt->m_nAge);
#endif
		pt->m_nAge = 0;
		t = *pt;
		if(pt->m_fTemp) delete pt;
		return true;
	}

	return false;
}

bool GSTextureCache::FetchNP(GSState* s, GSTextureBase& t)
{
	GSTexture* pt = NULL;

	int nPaletteEntries = GSLocalMemory::m_psmtbl[s->m_ctxt->TEX0.PSM].pal;

	DWORD clut[256];

	if(nPaletteEntries)
	{
		s->m_lm.SetupCLUT(s->m_ctxt->TEX0, s->m_de.TEXA);
		s->m_lm.CopyCLUT32(clut, nPaletteEntries);
	}

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 Fetch %dx%d %05I64x %I64d (%d)\n"), 
		1 << s->m_ctxt->TEX0.TW, 1 << s->m_ctxt->TEX0.TH, 
		s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM, nPaletteEntries);
#endif

	enum lookupresult {notfound, needsupdate, found} lr = notfound;

	POSITION pos = GetHeadPosition();
	while(pos && !pt)
	{
		POSITION cur = pos;
		pt = GetNext(pos);

		if(HasSharedBits(pt->m_TEX0.TBP0, pt->m_TEX0.PSM, s->m_ctxt->TEX0.TBP0, s->m_ctxt->TEX0.PSM))
		{
			if(pt->m_fRT)
			{
				lr = found;

				if(!(pt = ConvertRT(s, pt)))
					return false;
			}
			else if(s->m_ctxt->TEX0.PSM == pt->m_TEX0.PSM && pt->m_TEX0.TBW == s->m_ctxt->TEX0.TBW
			&& s->m_ctxt->TEX0.TW == pt->m_TEX0.TW && s->m_ctxt->TEX0.TH == pt->m_TEX0.TH
			&& (!(s->m_ctxt->CLAMP.WMS&2) && !(pt->m_CLAMP.WMS&2) && !(s->m_ctxt->CLAMP.WMT&2) && !(pt->m_CLAMP.WMT&2) || s->m_ctxt->CLAMP.i64 == pt->m_CLAMP.i64)
			// && s->m_de.TEXA.TA0 == pt->m_TEXA.TA0 && s->m_de.TEXA.TA1 == pt->m_TEXA.TA1 && s->m_de.TEXA.AEM == pt->m_TEXA.AEM
			&& (!nPaletteEntries || s->m_ctxt->TEX0.CPSM == pt->m_TEX0.CPSM && !memcmp(pt->m_clut, clut, nPaletteEntries*sizeof(clut[0]))))
			{
				lr = needsupdate;
			}
		}

		if(lr != notfound) {MoveToHead(cur); break;}

		pt = NULL;
	}

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 lr = %s\n"), lr == found ? _T("found") : lr == needsupdate ? _T("needsupdate") : _T("notfound"));
#endif

	if(lr == notfound)
	{
		pt = new GSTexture();

		pt->m_TEX0 = s->m_ctxt->TEX0;
		pt->m_CLAMP = s->m_ctxt->CLAMP;
		// pt->m_TEXA = s->m_de.TEXA;

		DWORD psm = s->m_ctxt->TEX0.PSM;

		switch(psm)
		{
		case PSM_PSMT8:
		case PSM_PSMT8H:
		case PSM_PSMT4:
		case PSM_PSMT4HL:
		case PSM_PSMT4HH:
			psm = s->m_ctxt->TEX0.CPSM;
			break;
		}

		if(!SUCCEEDED(CreateTexture(s, pt, psm)))
		{
			delete pt;
			return false;
		}

		RemoveOldTextures(s);

		AddHead(pt);

		lr = needsupdate;
	}

	ASSERT(pt);

	if(pt && nPaletteEntries)
	{
		memcpy(pt->m_clut, clut, nPaletteEntries*sizeof(clut[0]));
	}

	if(lr == needsupdate)
	{
		UpdateTexture(s, pt, &GSLocalMemory::ReadTextureNP);

		lr = found;
	}

	if(lr == found)
	{
#ifdef DEBUG_LOG
		s->LOG(_T("*TC2 texture was found, age %d -> 0\n"), pt->m_nAge);
#endif
		pt->m_nAge = 0;
		t = *pt;
		if(pt->m_fTemp) delete pt;
		return true;
	}

	return false;
}

void GSTextureCache::IncAge(CSurfMap<IDirect3DTexture9>& pRTs)
{
	POSITION pos = GetHeadPosition();
	while(pos)
	{
		POSITION cur = pos;
		GSTexture* pt = GetNext(pos);
		pt->m_nAge++;
		pt->m_nVsyncs++;
		if(pt->m_nAge > 10 && (!pt->m_fRT || pRTs.GetCount() > 3))
		{
			pRTs.RemoveKey(pt->m_TEX0.TBP0);
			RemoveAt(cur);
			delete pt;
		}
	}
}

void GSTextureCache::ResetAge(DWORD TBP0)
{
	POSITION pos = GetHeadPosition();
	while(pos)
	{
		GSTexture* pt = GetNext(pos);
		if(pt->m_TEX0.TBP0 == TBP0) pt->m_nAge = 0;
	}
}

void GSTextureCache::RemoveAll()
{
	while(GetCount()) delete RemoveHead();
	m_pTexturePool.RemoveAll();
	m_pRTPool.RemoveAll();
}

void GSTextureCache::InvalidateTexture(GSState* s, const GIFRegBITBLTBUF& BITBLTBUF, const CRect& r)
{
	GIFRegTEX0 TEX0;
	TEX0.TBP0 = BITBLTBUF.DBP;
	TEX0.TBW = BITBLTBUF.DBW;
	TEX0.PSM = BITBLTBUF.DPSM;
	TEX0.TCC = 0;

#ifdef DEBUG_LOG
	s->LOG(_T("*TC2 invalidate %05x %x (%d,%d-%d,%d)\n"), TEX0.TBP0, TEX0.PSM, r.left, r.top, r.right, r.bottom);
#endif

	POSITION pos = GetHeadPosition();
	while(pos)
	{
		POSITION cur = pos;
		GSTexture* pt = GetNext(pos);
		if(HasSharedBits(TEX0.TBP0, TEX0.PSM, pt->m_TEX0.TBP0, pt->m_TEX0.PSM)) 
		{
			if(TEX0.TBW != pt->m_TEX0.TBW)
			{
				// if TEX0.TBW != pt->m_TEX0.TBW then this render target is more likely to 
				// be discarded by the game (means it doesn't want to transfer an image over 
				// another pre-rendered image) and can be refetched from local mem safely.

				RemoveAt(cur);
				delete pt;
			}
			else if(pt->m_fRT) 
			{
				// TEX0.TBW = pt->m_TEX0.TBW;
				TEX0.PSM = pt->m_TEX0.PSM;

				if(TEX0.PSM == PSM_PSMCT32 || TEX0.PSM == PSM_PSMCT24 
				|| TEX0.PSM == PSM_PSMCT16 || TEX0.PSM == PSM_PSMCT16S) 
				{
//					pt->m_rcDirty.AddHead(GSDirtyRect(PSM, r));

					HRESULT hr;

					int tw = (r.Width() + 3) & ~3;
					int th = r.Height();

					CComPtr<IDirect3DTexture9> pSrc;
					hr = s->m_pD3DDev->CreateTexture(tw, th, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pSrc, NULL);

					D3DLOCKED_RECT lr;
					if(pSrc && SUCCEEDED(pSrc->LockRect(0, &lr, NULL, 0)))
					{
						GIFRegTEXA TEXA;
						TEXA.AEM = 1;
						TEXA.TA0 = 0;
						TEXA.TA1 = 0x80;

						GIFRegCLAMP CLAMP;
						CLAMP.WMS = 0;
						CLAMP.WMT = 0;

						s->m_lm.ReadTexture(r, (BYTE*)lr.pBits, lr.Pitch, TEX0, TEXA, CLAMP);
						s->m_perfmon.IncCounter(GSPerfMon::c_unswizzle, r.Width()*r.Height()*4);

						pSrc->UnlockRect(0);

						scale_t scale(pt->m_pTexture);

						CRect dst;
						dst.left = (long)(scale.x * r.left + 0.5);
						dst.top = (long)(scale.y * r.top + 0.5);
						dst.right = (long)(scale.x * r.right + 0.5);
						dst.bottom = (long)(scale.y * r.bottom + 0.5);

						//

						CComPtr<IDirect3DSurface9> pRTSurf;
						hr = pt->m_pTexture->GetSurfaceLevel(0, &pRTSurf);
						hr = s->m_pD3DDev->SetRenderTarget(0, pRTSurf);
						hr = s->m_pD3DDev->SetDepthStencilSurface(NULL);

						hr = s->m_pD3DDev->SetTexture(0, pSrc);
						hr = s->m_pD3DDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
						hr = s->m_pD3DDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_ZENABLE, FALSE);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
						hr = s->m_pD3DDev->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RGBA);

						hr = s->m_pD3DDev->SetPixelShader(NULL);

						struct
						{
							float x, y, z, rhw;
							float tu, tv;
						}
						pVertices[] =
						{
							{(float)dst.left, (float)dst.top, 0.5f, 2.0f, 0, 0},
							{(float)dst.right, (float)dst.top, 0.5f, 2.0f, 1.0f * r.Width() / tw, 0},
							{(float)dst.left, (float)dst.bottom, 0.5f, 2.0f, 0, 1},
							{(float)dst.right, (float)dst.bottom, 0.5f, 2.0f, 1.0f * r.Width() / tw, 1},
						};

						hr = s->m_pD3DDev->BeginScene();
						hr = s->m_pD3DDev->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
						hr = s->m_pD3DDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pVertices, sizeof(pVertices[0]));
						hr = s->m_pD3DDev->EndScene();

					}
				}
				else
				{
					RemoveAt(cur);
					delete pt;
				}
			}
			else
			{
				pt->m_rcDirty.AddHead(GSDirtyRect(TEX0.PSM, r));
			}
		}
	}
}

void GSTextureCache::InvalidateLocalMem(GSState* s, DWORD TBP0, DWORD BW, DWORD PSM, const CRect& r)
{
	CComPtr<IDirect3DTexture9> pRT;

	POSITION pos = GetHeadPosition();
	while(pos)
	{
		POSITION cur = pos;
		GSTexture* pt = GetNext(pos);
		if(pt->m_TEX0.TBP0 == TBP0 && pt->m_fRT) 
		{
			pRT = pt->m_pTexture;
			break;
		}
	}

	if(!pRT) return;

	// TODO: add resizing
/*
	HRESULT hr;

	D3DSURFACE_DESC desc;
	hr = pRT->GetLevelDesc(0, &desc);
	if(FAILED(hr)) return;

	CComPtr<IDirect3DSurface9> pVidMem;
	hr = pRT->GetSurfaceLevel(0, &pVidMem);
	if(FAILED(hr)) return;

	CComPtr<IDirect3DSurface9> pSysMem;
	hr = s->m_pD3DDev->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pSysMem, NULL);
	if(FAILED(hr)) return;

	hr = s->m_pD3DDev->GetRenderTargetData(pVidMem, pSysMem);
	if(FAILED(hr)) return;

	D3DLOCKED_RECT lr;
	hr = pSysMem->LockRect(&lr, &r, D3DLOCK_READONLY|D3DLOCK_NO_DIRTY_UPDATE);
	if(SUCCEEDED(hr))
	{
		BYTE* p = (BYTE*)lr.pBits;

		if(0 && r.left == 0 && r.top == 0 && PSM == PSM_PSMCT32)
		{
		}
		else
		{
			GSLocalMemory::writeFrame wf = s->m_lm.GetWriteFrame(PSM);

			for(int y = r.top; y < r.bottom; y++, p += lr.Pitch)
			{
				for(int x = r.left; x < r.right; x++)
				{
					(s->m_lm.*wf)(x, y, ((DWORD*)p)[x], TBP0, BW);
				}
			}
		}

		pSysMem->UnlockRect();
	}
	*/
}

void GSTextureCache::AddRT(GIFRegTEX0& TEX0, IDirect3DTexture9* pRT, scale_t scale)
{
	POSITION pos = GetHeadPosition();
	while(pos)
	{
		POSITION cur = pos;
		GSTexture* pt = GetNext(pos);
		if(HasSharedBits(TEX0.TBP0, TEX0.PSM, pt->m_TEX0.TBP0, pt->m_TEX0.PSM))
		{
			RemoveAt(cur);
			delete pt;
		}
	}

	GSTexture* pt = new GSTexture();
	pt->m_TEX0 = TEX0;
	pt->m_pTexture = pRT;
	pt->m_pTexture->GetLevelDesc(0, &pt->m_desc);
	pt->m_scale = scale;
	pt->m_fRT = true;

	AddHead(pt);
}




See more files for this project here

guliverkli

Home of VobSub, Media Player Classic (MPC) and other misc utils.

Project homepage: http://sourceforge.net/projects/guliverkli
Programming language(s): C,C++,PHP
License: other

  res/
    GSdx9.rc2
    hlsl_merge.fx
    hlsl_rb.fx
    hlsl_tfx.fx
    logo1.bmp
    ps11_en00.psh
    ps11_en01.psh
    ps11_en10.psh
    ps11_en11.psh
    ps11_tfx000.psh
    ps11_tfx010.psh
    ps11_tfx011.psh
    ps11_tfx1x0.psh
    ps11_tfx1x1.psh
    ps11_tfx200.psh
    ps11_tfx210.psh
    ps11_tfx211.psh
    ps11_tfx300.psh
    ps11_tfx310.psh
    ps11_tfx311.psh
    ps11_tfx4xx.psh
    ps14_en00.psh
    ps14_en01.psh
    ps14_en10.psh
    ps14_en11.psh
  GS.cpp
  GS.h
  GSCapture.cpp
  GSCapture.h
  GSCaptureDlg.cpp
  GSCaptureDlg.h
  GSHash.cpp
  GSHash.h
  GSLocalMemory.cpp
  GSLocalMemory.h
  GSPerfMon.cpp
  GSPerfMon.h
  GSRegs.cpp
  GSRenderer.cpp
  GSRenderer.h
  GSRendererHW.cpp
  GSRendererHW.h
  GSRendererNull.cpp
  GSRendererNull.h
  GSRendererSoft.cpp
  GSRendererSoft.h
  GSSettingsDlg.cpp
  GSSettingsDlg.h
  GSSoftVertex.cpp
  GSSoftVertex.h
  GSState.cpp
  GSState.h
  GSTables.cpp
  GSTables.h
  GSTextureCache.cpp
  GSTextureCache.h
  GSTransfer.cpp
  GSUtil.cpp
  GSUtil.h
  GSVertexList.cpp
  GSVertexList.h
  GSWnd.cpp
  GSWnd.h
  GSdx9.cpp
  GSdx9.def
  GSdx9.h
  GSdx9.icproj
  GSdx9.rc
  GSdx9.sln
  GSdx9.vcproj
  GSdx9_ic.sln
  GSdx9_vs2005.sln
  GSdx9_vs2005.vcproj
  resource.h
  stdafx.cpp
  stdafx.h
  x86-32.asm
  x86-64.asm
  x86.cpp
  x86.h