/* * 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" // static FILE* m_log = NULL; GSTextureCacheSW::GSTextureCacheSW(GSState* state) : m_state(state) { // m_log = _tfopen(_T("c:\\log.txt"), _T("w")); } GSTextureCacheSW::~GSTextureCacheSW() { // fclose(m_log); 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 CAtlList& t2p = m_p2t[TEX0.TBP0 >> 5]; // fprintf(m_log, "lu %05x %d %d (%d) ", TEX0.TBP0, TEX0.TBW, TEX0.PSM, t2p.GetCount()); // if(r) fprintf(m_log, "(%d %d %d %d) ", r->left, r->top, r->right, r->bottom); GSTexture* t = NULL; POSITION pos = t2p.GetHeadPosition(); while(pos) { GSTexture* t2 = t2p.GetNext(pos)->t; // 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 == 1 && !(t2->m_TEXA == (GSVector4i)TEXA).alltrue())) { continue; } // fprintf(m_log, "cache hit\n"); t = t2; t->m_age = 0; break; } if(t == NULL) { // fprintf(m_log, "cache miss\n"); 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; for(int j = 0, y = 0; y < th; j++, y += psm.pgs.cy) { DWORD page = psm.pgn(0, y, bp, bw); for(int i = 0, x = 0; x < tw && page < MAX_PAGES; i++, x += psm.pgs.cx, page++) { GSTexturePage* p = new GSTexturePage(); p->t = t; p->row = j; p->col = i; GSTexturePageEntry* p2te = new GSTexturePageEntry(); p2te->p2t = &m_p2t[page]; p2te->pos = m_p2t[page].AddHead(p); t->m_p2te.AddTail(p2te); t->m_maxpages++; } } } if(!t->Update(TEX0, TEXA, r)) { m_textures.RemoveAt(t->m_pos); delete t; printf("!@#$%\n"); // memory allocation may fail if the game is too hungry 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++) { CAtlList& t2p = m_p2t[i]; ASSERT(t2p.IsEmpty()); POSITION pos = t2p.GetHeadPosition(); while(pos) { delete t2p.GetNext(pos); } t2p.RemoveAll(); } } void GSTextureCacheSW::IncAge() { POSITION pos = m_textures.GetHeadPosition(); while(pos) { POSITION cur = pos; GSTexture* t = m_textures.GetNext(pos); if(++t->m_age > 3) { m_textures.RemoveAt(cur); delete t; } } } void GSTextureCacheSW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const CRect& r) { const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[BITBLTBUF.DPSM]; CRect r2; r2.left = r.left & ~(psm.pgs.cx - 1); r2.top = r.top & ~(psm.pgs.cy - 1); r2.right = (r.right + (psm.pgs.cx - 1)) & ~(psm.pgs.cx - 1); r2.bottom = (r.bottom + (psm.pgs.cy - 1)) & ~(psm.pgs.cy - 1); DWORD bp = BITBLTBUF.DBP; DWORD bw = BITBLTBUF.DBW; // fprintf(m_log, "ivm %05x %d %d (%d %d %d %d)\n", bp, bw, BITBLTBUF.DPSM, r2.left, r2.top, r2.right, r2.bottom); for(int y = r2.top; y < r2.bottom; y += psm.pgs.cy) { DWORD page = psm.pgn(r2.left, y, bp, bw); for(int x = r2.left; x < r2.right && page < MAX_PAGES; x += psm.pgs.cx, page++) { const CAtlList& t2p = m_p2t[page]; POSITION pos = t2p.GetHeadPosition(); while(pos) { GSTexturePage* p = t2p.GetNext(pos); DWORD flag = 1 << p->col; if((p->t->m_valid[p->row] & flag) == 0) { continue; } if(GSUtil::HasSharedBits(BITBLTBUF.DPSM, p->t->m_TEX0.PSM)) { p->t->m_valid[p->row] &= ~flag; p->t->m_pages--; // fprintf(m_log, "ivm hit %05x %d %d (%d %d) (%d)", p->t->m_TEX0.TBP0, p->t->m_TEX0.TBW, p->t->m_TEX0.PSM, p->row, p->col, p->t->m_pages); // if(p->t->m_pages == 0) fprintf(m_log, " *"); // fprintf(m_log, "\n"); } } } } } // GSTextureCacheSW::GSTexture::GSTexture(GSState* state) : m_state(state) , m_buff(NULL) , m_tw(0) , m_maxpages(0) , m_pages(0) , m_pos(NULL) , m_age(0) { memset(m_valid, 0, sizeof(m_valid)); } GSTextureCacheSW::GSTexture::~GSTexture() { if(m_buff) { _aligned_free(m_buff); } POSITION pos = m_p2te.GetHeadPosition(); while(pos) { GSTexturePageEntry* p2te = m_p2te.GetNext(pos); GSTexturePage* p = p2te->p2t->GetAt(p2te->pos); ASSERT(p->t == this); delete p; p2te->p2t->RemoveAt(p2te->pos); delete p2te; } m_p2te.RemoveAll(); } bool GSTextureCacheSW::GSTexture::Update(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const CRect* r) { if(m_pages == m_maxpages) { return true; } m_TEX0 = TEX0; m_TEXA = TEXA; GSLocalMemory& mem = m_state->m_mem; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; int tw = 1 << TEX0.TW; int th = 1 << TEX0.TH; if(tw < psm.bs.cx) tw = psm.bs.cx; if(th < psm.bs.cy) th = psm.bs.cy; if(m_buff == NULL) { // fprintf(m_log, "up new (%d %d)\n", tw, th); 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 r2; if(r) { r2.left = r->left & ~(psm.pgs.cx - 1); r2.top = r->top & ~(psm.pgs.cy - 1); r2.right = (r->right + (psm.pgs.cx - 1)) & ~(psm.pgs.cx - 1); r2.bottom = (r->bottom + (psm.pgs.cy - 1)) & ~(psm.pgs.cy - 1); } // TODO GSLocalMemory::readTexture rt = psm.pal > 0 ? psm.rtxP : psm.rtx; int bytes = psm.pal > 0 ? 1 : 4; BYTE* dst = (BYTE*)m_buff; DWORD pitch = (1 << m_tw) * bytes; DWORD mask = pitch - 1; for(int j = 0, y = 0; y < th; j++, y += psm.pgs.cy, dst += pitch * psm.pgs.cy) { if(m_valid[j] == mask) { continue; } if(r) { if(y < r2.top) continue; if(y >= r2.bottom) break; } DWORD page = psm.pgn(0, y, TEX0.TBP0, TEX0.TBW); for(int i = 0, x = 0; x < tw && page < MAX_PAGES; i++, x += psm.pgs.cx, page++) { if(r) { if(x < r2.left) continue; if(x >= r2.right) break; } DWORD flag = 1 << i; if(m_valid[j] & flag) { continue; } m_valid[j] |= flag; m_pages++; ASSERT(m_pages <= m_maxpages); CRect r; r.left = x; r.top = y; r.right = min(x + psm.pgs.cx, tw); r.bottom = min(y + psm.pgs.cy, th); // fprintf(m_log, "up fetch (%d %d) (%d %d %d %d)\n", j, i, r.left, r.top, r.right, r.bottom); (mem.*rt)(r, &dst[x * bytes], pitch, TEX0, TEXA); m_state->m_perfmon.Put(GSPerfMon::Unswizzle, r.Width() * r.Height() * bytes); } } return true; }