mirror of https://github.com/PCSX2/pcsx2.git
GS-SW: Fix framebuffer looping at 2048 height.
Also limit height read on hardware to 2048
This commit is contained in:
parent
b036dcece6
commit
de8b23db4e
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue