mirror of https://github.com/PCSX2/pcsx2.git
344 lines
6.8 KiB
C++
344 lines
6.8 KiB
C++
/*
|
|
* Copyright (C) 2007-2009 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 "GSTextureCacheSW.h"
|
|
|
|
GSTextureCacheSW::GSTextureCacheSW(GSState* state)
|
|
: m_state(state)
|
|
{
|
|
}
|
|
|
|
GSTextureCacheSW::~GSTextureCacheSW()
|
|
{
|
|
RemoveAll();
|
|
}
|
|
|
|
const GSTextureCacheSW::GSTexture* GSTextureCacheSW::Lookup(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const CRect* r)
|
|
{
|
|
GSLocalMemory& mem = m_state->m_mem;
|
|
|
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
|
|
|
const CAtlMap<GSTexture*, bool>& map = m_map[TEX0.TBP0 >> 5];
|
|
|
|
GSTexture* t = NULL;
|
|
|
|
POSITION pos = map.GetStartPosition();
|
|
|
|
while(pos)
|
|
{
|
|
GSTexture* t2 = map.GetNextKey(pos);
|
|
|
|
// if(t2->m_TEX0.TBP0 != TEX0.TBP0 || t2->m_TEX0.TBW != TEX0.TBW || t2->m_TEX0.PSM != TEX0.PSM || t2->m_TEX0.TW != TEX0.TW || t2->m_TEX0.TH != TEX0.TH)
|
|
if(((t2->m_TEX0.ai32[0] ^ TEX0.ai32[0]) | ((t2->m_TEX0.ai32[1] ^ TEX0.ai32[1]) & 3)) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if((psm.trbpp == 16 || psm.trbpp == 24) && (t2->m_TEX0.TCC != TEX0.TCC || TEX0.TCC && !(t2->m_TEXA == (GSVector4i)TEXA).alltrue()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
t = t2;
|
|
|
|
t->m_age = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
if(t == NULL)
|
|
{
|
|
t = new GSTexture(m_state);
|
|
|
|
t->m_pos = m_textures.AddTail(t);
|
|
|
|
int tw = 1 << TEX0.TW;
|
|
int th = 1 << TEX0.TH;
|
|
|
|
DWORD bp = TEX0.TBP0;
|
|
DWORD bw = TEX0.TBW;
|
|
|
|
CSize s = (bp & 31) == 0 ? psm.pgs : psm.bs;
|
|
|
|
for(int y = 0; y < th; y += s.cy)
|
|
{
|
|
DWORD base = psm.bn(0, y, bp, bw);
|
|
|
|
for(int x = 0; x < tw; x += s.cx)
|
|
{
|
|
DWORD page = (base + psm.blockOffset[x >> 3]) >> 5;
|
|
|
|
if(page >= MAX_PAGES)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
m_map[page].SetAt(t, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!t->Update(TEX0, TEXA, r))
|
|
{
|
|
printf("!@#$%\n"); // memory allocation may fail if the game is too hungry
|
|
|
|
m_textures.RemoveAt(t->m_pos);
|
|
|
|
for(int i = 0; i < MAX_PAGES; i++)
|
|
{
|
|
m_map[i].RemoveKey(t);
|
|
}
|
|
|
|
delete t;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
void GSTextureCacheSW::RemoveAll()
|
|
{
|
|
POSITION pos = m_textures.GetHeadPosition();
|
|
|
|
while(pos)
|
|
{
|
|
delete m_textures.GetNext(pos);
|
|
}
|
|
|
|
m_textures.RemoveAll();
|
|
|
|
for(int i = 0; i < MAX_PAGES; i++)
|
|
{
|
|
m_map[i].RemoveAll();
|
|
}
|
|
}
|
|
|
|
void GSTextureCacheSW::IncAge()
|
|
{
|
|
POSITION pos = m_textures.GetHeadPosition();
|
|
|
|
while(pos)
|
|
{
|
|
POSITION cur = pos;
|
|
|
|
GSTexture* t = m_textures.GetNext(pos);
|
|
|
|
if(++t->m_age > 30)
|
|
{
|
|
m_textures.RemoveAt(cur);
|
|
|
|
for(int i = 0; i < MAX_PAGES; i++)
|
|
{
|
|
m_map[i].RemoveKey(t);
|
|
}
|
|
|
|
delete t;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GSTextureCacheSW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const CRect& rect)
|
|
{
|
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[BITBLTBUF.DPSM];
|
|
|
|
DWORD bp = BITBLTBUF.DBP;
|
|
DWORD bw = BITBLTBUF.DBW;
|
|
|
|
CSize s = (bp & 31) == 0 ? psm.pgs : psm.bs;
|
|
|
|
CRect r;
|
|
|
|
r.left = rect.left & ~(s.cx - 1);
|
|
r.top = rect.top & ~(s.cy - 1);
|
|
r.right = (rect.right + (s.cx - 1)) & ~(s.cx - 1);
|
|
r.bottom = (rect.bottom + (s.cy - 1)) & ~(s.cy - 1);
|
|
|
|
for(int y = r.top; y < r.bottom; y += s.cy)
|
|
{
|
|
DWORD base = psm.bn(0, y, bp, bw);
|
|
|
|
for(int x = r.left; x < r.right; x += s.cx)
|
|
{
|
|
DWORD page = (base + psm.blockOffset[x >> 3]) >> 5;
|
|
|
|
if(page >= MAX_PAGES)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const CAtlMap<GSTexture*, bool>& map = m_map[page];
|
|
|
|
POSITION pos = map.GetStartPosition();
|
|
|
|
while(pos)
|
|
{
|
|
GSTexture* t = map.GetNextKey(pos);
|
|
|
|
t->m_valid[page] = 0;
|
|
|
|
t->m_complete = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
GSTextureCacheSW::GSTexture::GSTexture(GSState* state)
|
|
: m_state(state)
|
|
, m_buff(NULL)
|
|
, m_tw(0)
|
|
, m_age(0)
|
|
, m_pos(NULL)
|
|
, m_complete(false)
|
|
{
|
|
memset(m_valid, 0, sizeof(m_valid));
|
|
}
|
|
|
|
GSTextureCacheSW::GSTexture::~GSTexture()
|
|
{
|
|
if(m_buff)
|
|
{
|
|
_aligned_free(m_buff);
|
|
}
|
|
}
|
|
|
|
bool GSTextureCacheSW::GSTexture::Update(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const CRect* rect)
|
|
{
|
|
if(m_complete)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
m_TEX0 = TEX0;
|
|
m_TEXA = TEXA;
|
|
|
|
GSLocalMemory& mem = m_state->m_mem;
|
|
|
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
|
|
|
DWORD bp = TEX0.TBP0;
|
|
DWORD bw = TEX0.TBW;
|
|
|
|
CSize s = psm.bs;
|
|
|
|
int tw = max(1 << TEX0.TW, s.cx);
|
|
int th = max(1 << TEX0.TH, s.cy);
|
|
|
|
if(m_buff == NULL)
|
|
{
|
|
m_buff = _aligned_malloc(tw * th * sizeof(DWORD), 16);
|
|
|
|
if(m_buff == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_tw = max(psm.pal > 0 ? 5 : 3, TEX0.TW); // makes one row 32 bytes at least, matches the smallest block size that is allocated above for m_buff
|
|
}
|
|
|
|
CRect r(0, 0, tw, th);
|
|
|
|
if(rect)
|
|
{
|
|
r.left = rect->left & ~(s.cx - 1);
|
|
r.top = rect->top & ~(s.cy - 1);
|
|
r.right = (rect->right + (s.cx - 1)) & ~(s.cx - 1);
|
|
r.bottom = (rect->bottom + (s.cy - 1)) & ~(s.cy - 1);
|
|
}
|
|
|
|
if(r.left == 0 && r.top == 0 && r.right == tw && r.bottom == th)
|
|
{
|
|
m_complete = true; // lame, but better than nothing
|
|
}
|
|
|
|
GSLocalMemory::readTextureBlock rtxb = psm.rtxbP;
|
|
|
|
int bytes = psm.pal > 0 ? 1 : 4;
|
|
|
|
DWORD pitch = (1 << m_tw) * bytes;
|
|
|
|
BYTE* dst = (BYTE*)m_buff + pitch * r.top;
|
|
|
|
DWORD blocks = 0;
|
|
|
|
for(int y = r.top, o = pitch * s.cy; y < r.bottom; y += s.cy, dst += o)
|
|
{
|
|
DWORD base = psm.bn(0, y, bp, bw);
|
|
|
|
for(int x = r.left; x < r.right; x += s.cx)
|
|
{
|
|
DWORD block = base + psm.blockOffset[x >> 3];
|
|
|
|
if(block >= MAX_BLOCKS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DWORD row = block >> 5;
|
|
DWORD col = 1 << (block & 31);
|
|
|
|
if(m_valid[row] & col)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// unfortunatelly a block may be part of the same texture multiple times at different places (when (1 << tw) > (tbw << 6), ex. 1024 > 640),
|
|
// so just can't set the block's bit to valid in one pass, even if 99.9% of the games don't address the repeated part at the right side
|
|
|
|
// TODO: still bogus if those repeated parts aren't fetched together
|
|
|
|
// m_valid[row] |= col;
|
|
|
|
(mem.*rtxb)(block, &dst[x * bytes], pitch, TEXA);
|
|
|
|
blocks++;
|
|
}
|
|
}
|
|
|
|
m_state->m_perfmon.Put(GSPerfMon::Unswizzle, s.cx * s.cy * bytes * blocks);
|
|
|
|
for(int y = r.top; y < r.bottom; y += s.cy)
|
|
{
|
|
DWORD base = psm.bn(0, y, bp, bw);
|
|
|
|
for(int x = r.left; x < r.right; x += s.cx)
|
|
{
|
|
DWORD block = base + psm.blockOffset[x >> 3];
|
|
|
|
if(block >= MAX_BLOCKS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DWORD row = block >> 5;
|
|
DWORD col = 1 << (block & 31);
|
|
|
|
m_valid[row] |= col;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|