GS/HW: Allow creation of known targets via move with offset

Fixes upscaling with subtitles in Devil May Cry.
This commit is contained in:
Stenzek 2024-04-14 20:03:24 +10:00 committed by Connor McLaughlin
parent 070068366f
commit 83bf215ead
3 changed files with 46 additions and 1 deletions

View File

@ -2310,6 +2310,24 @@ void GSRendererHW::Draw()
GL_INS("Skipping (%d,%d=>%d,%d) draw at FBP %x/ZBP %x due to invalid height or zero clear.", m_r.x, m_r.y,
m_r.z, m_r.w, m_cached_ctx.FRAME.Block(), m_cached_ctx.ZBUF.Block());
// Since we're not creating a target here, if this is the first draw to the target, it's not going
// to be in the height cache, and we might create a smaller size target. We also need to record
// it for HW moves (e.g. Devil May Cry subtitles).
if (!height_invalid)
{
const GSVector2i target_size = GetValidSize(nullptr);
if (!no_rt && is_zero_color_clear)
{
g_texture_cache->GetTargetSize(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM,
target_size.x, target_size.y);
}
if (!no_ds && is_zero_depth_clear)
{
g_texture_cache->GetTargetSize(m_cached_ctx.ZBUF.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.ZBUF.PSM,
target_size.x, target_size.y);
}
}
CleanupDraw(false);
return;
}

View File

@ -3648,8 +3648,9 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// 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.
// We can also recreate the target if it's previously been created in the height cache with a valid size.
// Good test case for this is the Xenosaga I cutscene transitions, or Gradius V.
if (src && !dst && dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) <= DBW)
if (src && !dst && ((dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) <= DBW) || HasTargetInHeightCache(DBP, DBW, DPSM, 10)))
{
GIFRegTEX0 new_TEX0 = {};
new_TEX0.TBP0 = DBP;
@ -4107,6 +4108,31 @@ GSVector2i GSTextureCache::GetTargetSize(u32 bp, u32 fbw, u32 psm, s32 min_width
return GSVector2i(min_width, min_height);
}
bool GSTextureCache::HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age, bool move_front)
{
TargetHeightElem search = {};
search.bp = bp;
search.fbw = fbw;
search.psm = psm;
for (auto it = m_target_heights.begin(); it != m_target_heights.end(); ++it)
{
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
if (elem.bits == search.bits)
{
if (elem.age > max_age)
return false;
if (move_front)
m_target_heights.MoveFront(it.Index());
return true;
}
}
return false;
}
bool GSTextureCache::Has32BitTarget(u32 bp)
{
// Look for 32-bit targets at the matching block.

View File

@ -502,6 +502,7 @@ public:
Target* FindOverlappingTarget(u32 BP, u32 BW, u32 PSM, GSVector4i rc) const;
GSVector2i GetTargetSize(u32 bp, u32 fbw, u32 psm, s32 min_width, s32 min_height);
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
bool Has32BitTarget(u32 bp);
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32);