gsdx tc: implement a safe RemoveAt

The code is now a mirror of the ::add. So 1 insert == 1 erase

This way it won't crash on future update. And it will support future GS
memory wrapping improvement.
This commit is contained in:
Gregory Hainaut 2017-01-12 21:17:01 +01:00
parent d1315b6187
commit 87cf7b6d30
2 changed files with 21 additions and 11 deletions

View File

@ -1610,9 +1610,6 @@ GSTextureCache::Source::Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFR
{ {
m_p2t = r->m_mem.GetPage2TileMap(m_TEX0); m_p2t = r->m_mem.GetPage2TileMap(m_TEX0);
} }
// will be useless with texture page coverage
m_erase_it.fill(list<Source*>::iterator(0));
} }
} }
@ -1996,10 +1993,10 @@ void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0, GSOffset*
// Remaining code will compute a list of pages that are dirty (in a similar fashion as GSOffset::GetPages) // Remaining code will compute a list of pages that are dirty (in a similar fashion as GSOffset::GetPages)
// (Maybe GetPages could be used instead, perf opt?) // (Maybe GetPages could be used instead, perf opt?)
// The source pointer will be stored/duplicated in all m_map[array of pages] // The source pointer will be stored/duplicated in all m_map[array of pages]
uint32* pages = GetPagesCoverage(TEX0, off); s->m_pages_ptr = GetPagesCoverage(TEX0, off);
for(size_t i = 0; i < countof(m_pages); i++) for(size_t i = 0; i < countof(m_pages); i++)
{ {
if(uint32 p = pages[i]) if(uint32 p = s->m_pages_ptr[i])
{ {
list<Source*>* m = &m_map[i << 5]; list<Source*>* m = &m_map[i << 5];
auto* e = &s->m_erase_it[i << 5]; auto* e = &s->m_erase_it[i << 5];
@ -2093,18 +2090,30 @@ void GSTextureCache::SourceMap::RemoveAt(Source* s)
if (s->m_target) if (s->m_target)
{ {
size_t page = s->m_TEX0.TBP0 >> 5; size_t page = s->m_TEX0.TBP0 >> 5;
ASSERT(*s->m_erase_it[page] == s);
m_map[page].erase(s->m_erase_it[page]); m_map[page].erase(s->m_erase_it[page]);
} }
else else
{ {
// Source is duplicated for each page they use. // Mirror of GetPagesCoverage
for(size_t page = s->m_TEX0.TBP0 >> 5, end = countof(m_map) - 1; page <= end; page++) for(size_t i = 0; i < countof(m_pages); i++)
{ {
if (s->m_erase_it[page] != list<Source*>::iterator(0)) { if(uint32 p = s->m_pages_ptr[i])
ASSERT(*s->m_erase_it[page] == s); {
m_map[page].erase(s->m_erase_it[page]); list<Source*>* m = &m_map[i << 5];
auto* e = &s->m_erase_it[i << 5];
unsigned long j;
while(_BitScanForward(&j, p))
{
// FIXME: this statement could be optimized to a single ASM instruction (instead of 4)
// Either BTR (AKA bit test and reset). Depends on the previous instruction.
// Or BLSR (AKA Reset Lowest Set Bit). No dependency but require BMI1 (basically a recent CPU)
p ^= 1 << j;
m[j].erase(e[j]);
}
} }
} }
} }

View File

@ -74,6 +74,7 @@ public:
GIFRegTEX0 m_layer_TEX0[7]; // Detect already loaded value GIFRegTEX0 m_layer_TEX0[7]; // Detect already loaded value
// Keep an GSTextureCache::m_map iterator to allow fast erase // Keep an GSTextureCache::m_map iterator to allow fast erase
std::array<std::list<Source*>::iterator, MAX_PAGES> m_erase_it; std::array<std::list<Source*>::iterator, MAX_PAGES> m_erase_it;
uint32* m_pages_ptr;
public: public:
Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, uint8* temp, bool dummy_container = false); Source(GSRenderer* r, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, uint8* temp, bool dummy_container = false);