GS: Fix looping over pages of textures with massive strides

This commit is contained in:
TellowKrinkle 2021-12-17 20:17:53 -06:00 committed by tellowkrinkle
parent cb18f11133
commit 013c292ffb
2 changed files with 56 additions and 17 deletions

View File

@ -1819,6 +1819,8 @@ GSOffset::PageLooper GSOffset::pageLooperForRect(const GSVector4i& rect) const
out.yCnt = botPg - topPg;
out.firstRowPgXStart = out.midRowPgXStart = out.lastRowPgXStart = rect.left >> m_pageShiftX;
out.firstRowPgXEnd = out.midRowPgXEnd = out.lastRowPgXEnd = ((rect.right + m_pageMask.x) >> m_pageShiftX) + !aligned;
out.slowPath = static_cast<u32>(out.yCnt * out.yInc + out.midRowPgXEnd - out.midRowPgXStart) > MAX_PAGES;
// Page-aligned bp is easy, all tiles touch their lower page but not the upper
if (aligned)
return out;

View File

@ -310,6 +310,7 @@ public:
int bp; ///< Page offset of y=top x=0
int yInc; ///< Amount to add to bp when increasing y by one page
int yCnt; ///< Number of pages the rect covers in the y direction
bool slowPath; ///< True if the texture is big enough to wrap around GS memory and overlap itself
friend class GSOffset;
@ -320,30 +321,66 @@ public:
void loopPagesWithBreak(Fn&& fn) const
{
int lineBP = bp;
int nextMin = 0;
int startOff = firstRowPgXStart;
int endOff = firstRowPgXEnd;
int yCnt = this->yCnt;
for (int y = 0; y < yCnt; y++)
{
int start = std::max(nextMin, lineBP + startOff);
int end = lineBP + endOff;
nextMin = end;
lineBP += yInc;
for (int pos = start; pos < end; pos++)
if (!fn(pos % MAX_PAGES))
return;
if (y < yCnt - 1)
if (unlikely(slowPath))
{
u32 touched[MAX_PAGES / 32] = {};
for (int y = 0; y < yCnt; y++)
{
startOff = midRowPgXStart;
endOff = midRowPgXEnd;
u32 start = lineBP + startOff;
u32 end = lineBP + endOff;
lineBP += yInc;
for (u32 pos = start; pos < end; pos++)
{
u32 page = pos % MAX_PAGES;
u32 idx = page / 32;
u32 mask = 1 << (page % 32);
if (touched[idx] & mask)
continue;
if (!fn(page))
return;
touched[idx] |= mask;
}
if (y < yCnt - 1)
{
startOff = midRowPgXStart;
endOff = midRowPgXEnd;
}
else
{
startOff = lastRowPgXStart;
endOff = lastRowPgXEnd;
}
}
else
}
else
{
u32 nextMin = 0;
for (int y = 0; y < yCnt; y++)
{
startOff = lastRowPgXStart;
endOff = lastRowPgXEnd;
u32 start = std::max<u32>(nextMin, lineBP + startOff);
u32 end = lineBP + endOff;
nextMin = end;
lineBP += yInc;
for (u32 pos = start; pos < end; pos++)
if (!fn(pos % MAX_PAGES))
return;
if (y < yCnt - 1)
{
startOff = midRowPgXStart;
endOff = midRowPgXEnd;
}
else
{
startOff = lastRowPgXStart;
endOff = lastRowPgXEnd;
}
}
}
}