diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 971a842e3f..f44201ed6a 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -147,7 +147,8 @@ bool GSRenderer::Merge(int field) s_n++; - if (samesrc && fr[0].bottom == fr[1].bottom && !feedback_merge && fr[0].right == fr[1].right) + // Only need to check the right/bottom on software renderer, hardware always gets the full texture then cuts a bit out later. + if (samesrc && !feedback_merge && (GSConfig.UseHardwareRenderer() || (fr[0].right == fr[1].right && fr[0].bottom == fr[1].bottom))) { tex[0] = GetOutput(0, y_offset[0]); tex[1] = tex[0]; // saves one texture fetch @@ -200,11 +201,14 @@ 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 (!GSConfig.UseHardwareRenderer()) + { + // 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. @@ -239,11 +243,15 @@ bool GSRenderer::Merge(int field) if (display_diff.y < 4) off.y -= display_diff.y; - // Offset by DISPFB setting - if (abs(frame_diff.x) < 4) - off.x += frame_diff.x; - if (abs(frame_diff.y) < 4) - off.y += frame_diff.y; + // Only functional in HW mode, software clips/positions the framebuffer on read. + if (GSConfig.UseHardwareRenderer()) + { + // Offset by DISPFB setting + if (abs(frame_diff.x) < 4) + off.x += frame_diff.x; + if (abs(frame_diff.y) < 4) + off.y += frame_diff.y; + } } } } @@ -289,7 +297,8 @@ bool GSRenderer::Merge(int field) off.y += display_baseline.y; // Anti-Blur stuff - if (GSConfig.PCRTCAntiBlur) + // Only functional in HW mode, software clips/positions the framebuffer on read. + if (GSConfig.PCRTCAntiBlur && GSConfig.UseHardwareRenderer()) { // Offset by DISPFB setting if (abs(frame_diff.x) < 4) diff --git a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp index a8bc4255f1..6a2c9adf13 100644 --- a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp +++ b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp @@ -35,7 +35,6 @@ GSRendererSW::GSRendererSW(int threads) m_tc = std::make_unique(); m_rl = GSRasterizerList::Create(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); @@ -146,46 +145,52 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset) 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; + const GSVector4i out_r(0, 0, w, h); 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); + bool h_wrap = false; + bool w_wrap = false; // 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; + h_wrap = true; } if (r.right >= 2048) { r.right = 2048; rh.right = 2048; - part2_w = true; rw.left = 0; - + w_wrap = true; } + const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[DISPFB.PSM]; + // Top left rect (m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), r.ralign(psm.bs), m_output, pitch, m_env.TEXA); - 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; + int top = (h_wrap) ? ((r.bottom - r.top) * pitch) : 0; + int left = (w_wrap) ? (r.right - r.left) * (GSLocalMemory::m_psm[DISPFB.PSM].bpp / 8) : 0; - if (part2_w) + // The following only happen if the DBX/DBY wrap around at 2048. + + // Top right rect + if (w_wrap) (m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rw.ralign(psm.bs), &m_output[left], pitch, m_env.TEXA); - if (part2_h) + // Bottom left rect + if (h_wrap) (m_mem.*psm.rtx)(m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rh.ralign(psm.bs), &m_output[top], pitch, m_env.TEXA); - if (part2_h && part2_w) + // Bottom right rect + if (h_wrap && w_wrap) { - // needs also rw with the start/end height of rh, fills in the bottom right rect which will be missing if both overflow. + // 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(psm.bs), &m_output[top + left], pitch, m_env.TEXA); }