From 80dce398e0f3886823d0effc9089d76459a77285 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 11 Feb 2023 13:26:46 +1000 Subject: [PATCH] GS/HW: Carefully allow move to create new targets Xenosaga I does a move from BP 1C00 to E00, then from E00 to 2A00 a few frames later for its cutscene transitions. 2A00 then gets used as a texture and blended on top of the later frames. Because there's no target at 2A00, the move falls back to the CPU, and E00 contains junk which gets moved and eventually preloaded instead. Gradius V uses moves for a screen move/wobble-like effect, by moving chunks of the framebuffer out, then using those as a texture. It's not broken at the moment, but it does readback (slow), and break upscaling. --- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index f99cd0acf3..ea01eece56 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1579,6 +1579,24 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u // Look for an exact match on the targets. GSTextureCache::Target* src = GetExactTarget(SBP, SBW, SPSM); GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, DPSM); + + // Beware of the case where a game might create a larger texture by moving a bunch of chunks around. + // We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory. + // Good test case for this is the Xenosaga I cutscene transitions, or Gradius V. + if (src && !dst && dx == 0 && dy == 0 && ((static_cast(w) + 63) / 64) == DBW) + { + GIFRegTEX0 new_TEX0 = {}; + new_TEX0.TBP0 = DBP; + new_TEX0.TBW = DBW; + new_TEX0.PSM = DPSM; + + const int real_height = GetTargetHeight(DBP, DBW, DPSM, h); + const GSVector2 scale(src->m_texture->GetScale()); + dst = LookupTarget(new_TEX0, GSVector2i(static_cast(w * scale.x), static_cast(real_height * scale.y)), src->m_type, true); + if (dst) + dst->m_texture->SetScale(scale); + } + if (!src || !dst || src->m_texture->GetScale() != dst->m_texture->GetScale()) return false;