GS-SW: Fix framebuffer looping at 2048 height.

Also limit height read on hardware to 2048
This commit is contained in:
refractionpcsx2 2022-07-22 02:35:08 +01:00
parent b036dcece6
commit de8b23db4e
5 changed files with 76 additions and 26 deletions

View File

@ -507,11 +507,11 @@ GSVector2i GSState::GetResolution()
return resolution;
}
GSVector4i GSState::GetFrameRect(int i)
GSVector4i GSState::GetFrameRect(int i, bool ignore_off)
{
// If no specific context is requested then pass the merged rectangle as return value
if (i == -1)
return GetFrameRect(0).runion(GetFrameRect(1));
return GetFrameRect(0, ignore_off).runion(GetFrameRect(1, ignore_off));
GSVector4i rectangle = { 0, 0, 0, 0 };
@ -525,13 +525,19 @@ GSVector4i GSState::GetFrameRect(int i)
const GSVector2i magnification(DISP.MAGH+1, DISP.MAGV + 1);
const u32 DBX = m_regs->DISP[i].DISPFB.DBX;
const u32 DBY = m_regs->DISP[i].DISPFB.DBY;
int DBY = m_regs->DISP[i].DISPFB.DBY;
int w = DW / magnification.x;
int h = DH / magnification.y;
rectangle.left = DBX;
rectangle.top = DBY;
// If the combined height overflows 2048, it's likely adding a bit of extra data before the picture for offsetting the interlace
// only game known to do this is NASCAR '08
if (!ignore_off && (DBY + h) >= 2048)
DBY = DBY - 2048;
rectangle.left = (ignore_off) ? 0 : DBX;
rectangle.top = (ignore_off) ? 0 : DBY;
rectangle.right = rectangle.left + w;
rectangle.bottom = rectangle.top + h;
@ -550,9 +556,8 @@ int GSState::GetFramebufferHeight()
// Framebuffer height is 11 bits max
constexpr int height_limit = (1 << 11);
const GSVector4i disp1_rect = GetFrameRect(0);
const GSVector4i disp2_rect = GetFrameRect(1);
const GSVector4i disp1_rect = GetFrameRect(0, true);
const GSVector4i disp2_rect = GetFrameRect(1, true);
const GSVector4i combined = disp1_rect.runion(disp2_rect);
// DBY isn't an offset to the frame memory but rather an offset to read output circuit inside

View File

@ -327,7 +327,7 @@ public:
GSVector4i GetFrameMagnifiedRect(int i = -1);
GSVector2i GetResolutionOffset(int i = -1);
GSVector2i GetResolution();
GSVector4i GetFrameRect(int i = -1);
GSVector4i GetFrameRect(int i = -1, bool ignore_off = false);
GSVideoMode GetVideoMode();
bool IsEnabled(int i);

View File

@ -112,8 +112,8 @@ bool GSRenderer::Merge(int field)
display_baseline.x = std::min(display_offsets[i].x, display_baseline.x);
display_baseline.y = std::min(display_offsets[i].y, display_baseline.y);
frame_baseline.x = std::min(fr[i].left, frame_baseline.x);
frame_baseline.y = std::min(fr[i].top, frame_baseline.y);
frame_baseline.x = std::min(std::max(fr[i].left, 0), frame_baseline.x);
frame_baseline.y = std::min(std::max(fr[i].top, 0), frame_baseline.y);
display_offset |= std::abs(display_baseline.y - display_offsets[i].y) == 1;
/*DevCon.Warning("Read offset was X %d(left %d) Y %d(top %d)", display_baseline.x, dr[i].left, display_baseline.y, dr[i].top);
@ -147,7 +147,7 @@ bool GSRenderer::Merge(int field)
s_n++;
if (samesrc && fr[0].bottom == fr[1].bottom && !feedback_merge)
if (samesrc && fr[0].bottom == fr[1].bottom && !feedback_merge && fr[0].right == fr[1].right)
{
tex[0] = GetOutput(0, y_offset[0]);
tex[1] = tex[0]; // saves one texture fetch
@ -200,6 +200,12 @@ bool GSRenderer::Merge(int field)
GSVector2i display_diff(display_offsets[i].x - display_baseline.x, display_offsets[i].y - display_baseline.y);
GSVector2i frame_diff(fr[i].left - frame_baseline.x, fr[i].top - frame_baseline.y);
// Clear any frame offsets, we don't need them now.
fr[i].right -= fr[i].left;
fr[i].left = 0;
fr[i].bottom -= fr[i].top;
fr[i].top = 0;
// If using scanmsk we have to keep the single line offset, regardless of upscale
// so we handle this separately after the rect calculations.
float interlace_offset = 0.0f;
@ -234,10 +240,10 @@ bool GSRenderer::Merge(int field)
off.y -= display_diff.y;
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
if (abs(frame_diff.x) < 4)
off.x += frame_diff.x;
if (abs(frame_diff.y) < 4)
off.y += frame_diff.y;
}
}
}
@ -286,11 +292,10 @@ bool GSRenderer::Merge(int field)
if (GSConfig.PCRTCAntiBlur)
{
// Offset by DISPFB setting
if (frame_diff.x == 1)
off.x += 1;
if (frame_diff.y == 1)
off.y += 1;
if (abs(frame_diff.x) < 4)
off.x += frame_diff.x;
if (abs(frame_diff.y) < 4)
off.y += frame_diff.y;
}
}
}

View File

@ -275,7 +275,7 @@ GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
const int display_height = offsets.y * ((isinterlaced() && !m_regs->SMODE2.FFMD) ? 2 : 1);
const int display_offset = GetResolutionOffset(i).y;
int fb_height = std::min(GetFramebufferHeight(), display_height) + DISPFB.DBY;
int fb_height = std::min<int>(std::min<int>(GetFramebufferHeight(), display_height) + (int)DISPFB.DBY, 2048);
// If there is a negative vertical offset on the picture, we need to read more.
if (display_offset < 0)

View File

@ -35,6 +35,7 @@ GSRendererSW::GSRendererSW(int threads)
m_tc = std::make_unique<GSTextureCacheSW>();
m_rl = GSRasterizerList::Create<GSDrawScanline>(threads);
// Max framebuffer size can be 1024x2048 (it will loop at 2048).
m_output = (u8*)_aligned_malloc(1024 * 1024 * sizeof(u32), 32);
std::fill(std::begin(m_fzb_pages), std::end(m_fzb_pages), 0);
@ -132,7 +133,7 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
const int display_offset = GetResolutionOffset(i).y;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
const int display_height = offsets.y * ((isinterlaced() && !m_regs->SMODE2.FFMD) ? 2 : 1);
int h = std::min(GetFramebufferHeight(), display_height) + DISPFB.DBY;
int h = std::min(GetFramebufferHeight(), display_height);
// If there is a negative vertical offset on the picture, we need to read more.
if (display_offset < 0)
@ -142,15 +143,54 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
if (g_gs_device->ResizeTarget(&m_texture[i], w, h))
{
static int pitch = 1024 * 4;
constexpr int pitch = 1024 * 4;
const int off_x = DISPFB.DBX & 0x7ff;
const int off_y = DISPFB.DBY & 0x7ff;
bool part2_h = false;
bool part2_w = false;
GSVector4i r(off_x, off_y, w + off_x, h + off_y);
GSVector4i rh(off_x, off_y, w + off_x, (h + off_y) & 0x7FF);
GSVector4i rw(off_x, off_y, (w + off_x) & 0x7FF, h + off_y);
GSVector4i out_r(0, 0, w, h);
GSVector4i r(0, 0, w, h);
// Need to read it in 2 parts, since you can't do a split rect.
if (r.bottom >= 2048)
{
r.bottom = 2048;
rw.bottom = 2048;
part2_h = true;
rh.top = 0;
}
if (r.right >= 2048)
{
r.right = 2048;
rh.right = 2048;
part2_w = true;
rw.left = 0;
}
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[DISPFB.PSM];
(m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), r.ralign<Align_Outside>(psm.bs), m_output, pitch, m_env.TEXA);
m_texture[i]->Update(r, m_output, pitch);
int top = (part2_h) ? ((r.bottom - r.top) * pitch) : 0;
int left = (part2_w) ? (r.right - r.left) * (GSLocalMemory::m_psm[DISPFB.PSM].bpp / 8) : 0;
if (part2_w)
(m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rw.ralign<Align_Outside>(psm.bs), &m_output[left], pitch, m_env.TEXA);
if (part2_h)
(m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rh.ralign<Align_Outside>(psm.bs), &m_output[top], pitch, m_env.TEXA);
if (part2_h && part2_w)
{
// needs also rw with the start/end height of rh, fills in the bottom right rect which will be missing if both overflow.
const GSVector4i rwh(rw.left, rh.top, rw.right, rh.bottom);
(m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rwh.ralign<Align_Outside>(psm.bs), &m_output[top + left], pitch, m_env.TEXA);
}
m_texture[i]->Update(out_r, m_output, pitch);
if (s_dump)
{