GS-HW: Invalidate overlapped target when expanding.

This commit is contained in:
refractionpcsx2 2023-02-19 00:55:43 +00:00
parent faf36ecba6
commit 0c6e1a4d56
3 changed files with 110 additions and 6 deletions

View File

@ -1309,13 +1309,13 @@ void GSRendererHW::Draw()
// 2/ SuperMan really draws (0,0,0,0) color and a (0) 32-bits depth
// 3/ 50cents really draws (0,0,0,128) color and a (0) 24 bits depth
// Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0
const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1));
const bool no_ds = !no_rt && (
const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1))
|| (!context->TEST.DATE && (context->FRAME.FBMSK & GSLocalMemory::m_psm[context->FRAME.PSM].fmsk) == GSLocalMemory::m_psm[context->FRAME.PSM].fmsk);
const bool no_ds = (
// Depth is always pass/fail (no read) and write are discarded (tekken 5). (Note: DATE is currently implemented with a stencil buffer => a depth/stencil buffer)
(zm != 0 && m_context->TEST.ZTST <= ZTST_ALWAYS && !m_context->TEST.DATE) ||
// Depth will be written through the RT
(context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE)
);
(!no_rt && context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE));
if (no_rt && no_ds)
{
@ -1740,15 +1740,72 @@ void GSRendererHW::Draw()
if (rt)
{
const u32 old_end_block = rt->m_end_block;
const bool new_height = new_h > rt->m_texture->GetHeight();
const int old_height = rt->m_texture->GetHeight();
pxAssert(rt->m_texture->GetScale() == up_s);
rt->ResizeTexture(new_w, new_h, up_s);
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector4i new_valid = GSVector4i(0, 0, new_w / up_s.x, new_h / up_s.y);
rt->ResizeValidity(new_valid);
}
rt->UpdateValidity(m_r);
// Probably changing to double buffering, so invalidate any old target that was next to it.
// This resolves an issue where the PCRTC will find the old target in FMV's causing flashing.
// Grandia Xtreme, Onimusha Warlord.
if (new_height && old_end_block != rt->m_end_block)
{
GSTextureCache::Target* old_rt = nullptr;
old_rt = m_tc->FindTargetOverlap(old_end_block, rt->m_end_block, GSTextureCache::RenderTarget, context->FRAME.PSM);
if (old_rt && old_rt != rt && GSUtil::HasSharedBits(old_rt->m_TEX0.PSM, rt->m_TEX0.PSM))
{
const int copy_width = (old_rt->m_texture->GetWidth()) > (rt->m_texture->GetWidth()) ? (rt->m_texture->GetWidth()) : old_rt->m_texture->GetWidth();
const int copy_height = (old_rt->m_texture->GetHeight()) > (rt->m_texture->GetHeight() - old_height) ? (rt->m_texture->GetHeight() - old_height) : old_rt->m_texture->GetHeight();
g_gs_device->CopyRect(old_rt->m_texture, rt->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
m_tc->InvalidateVideoMemType(GSTextureCache::RenderTarget, old_rt->m_TEX0.TBP0);
old_rt = nullptr;
}
}
}
if (ds)
{
const u32 old_end_block = ds->m_end_block;
const bool new_height = new_h > ds->m_texture->GetHeight();
const int old_height = ds->m_texture->GetHeight();
pxAssert(ds->m_texture->GetScale() == up_s);
ds->ResizeTexture(new_w, new_h, up_s);
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector4i new_valid = GSVector4i(0, 0, new_w / up_s.x, new_h / up_s.y);
ds->ResizeValidity(new_valid);
}
ds->UpdateValidity(m_r);
if (new_height && old_end_block != ds->m_end_block)
{
GSTextureCache::Target* old_ds = nullptr;
old_ds = m_tc->FindTargetOverlap(old_end_block, ds->m_end_block, GSTextureCache::DepthStencil, context->ZBUF.PSM);
if (old_ds && old_ds != ds && GSUtil::HasSharedBits(old_ds->m_TEX0.PSM, ds->m_TEX0.PSM))
{
const int copy_width = (old_ds->m_texture->GetWidth()) > (ds->m_texture->GetWidth()) ? (ds->m_texture->GetWidth()) : old_ds->m_texture->GetWidth();
const int copy_height = (old_ds->m_texture->GetHeight()) > (ds->m_texture->GetHeight() - old_height) ? (ds->m_texture->GetHeight() - old_height) : old_ds->m_texture->GetHeight();
g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, old_ds->m_TEX0.TBP0);
old_ds = nullptr;
}
}
}
}
@ -1924,7 +1981,7 @@ void GSRendererHW::Draw()
s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
if (ds)
rt->m_texture->Save(s);
ds->m_texture->Save(s);
}
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)

View File

@ -581,6 +581,19 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
return src;
}
GSTextureCache::Target* GSTextureCache::FindTargetOverlap(u32 bp, u32 end_block, int type, int psm)
{
u32 end_block_bp = end_block < bp ? (MAX_BP + 1) : end_block;
for (auto t : m_dst[type])
{
// Only checks that the texure starts at the requested bp, which shares data. Size isn't considered.
if (t->m_TEX0.TBP0 >= bp && t->m_TEX0.TBP0 < end_block_bp && GSUtil::HasSharedBits(t->m_TEX0.PSM, psm))
return t;
}
return nullptr;
}
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_w, const int real_h, bool preload)
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
@ -3251,14 +3264,44 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc)
}
}
void GSTextureCache::Target::ResizeValidity(const GSVector4i& rect)
{
if (!m_valid.eq(GSVector4i::zero()))
{
m_valid = m_valid.rintersect(rect);
m_drawn_since_read = m_drawn_since_read.rintersect(rect);
}
else
{
m_valid = rect;
m_drawn_since_read = rect;
}
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
// TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and
// at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact
// that we clamp the draw rect to the target size in GSRendererHW::Draw().
m_end_block = GSLocalMemory::m_psm[m_TEX0.PSM].info.bn(m_valid.z - 1, m_valid.w - 1, m_TEX0.TBP0, m_TEX0.TBW); // Valid only for color formats
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
}
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
{
if (m_valid.runion(rect).eq(m_valid))
{
m_drawn_since_read = m_drawn_since_read.runion(rect);
return;
}
if (m_valid.eq(GSVector4i::zero()))
m_valid = rect;
else
m_valid = m_valid.runion(rect);
m_drawn_since_read = m_drawn_since_read.runion(rect);
if (m_drawn_since_read.eq(GSVector4i::zero()))
m_drawn_since_read = rect;
else
m_drawn_since_read = m_drawn_since_read.runion(rect);
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
// TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and
// at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact
@ -3284,6 +3327,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect
// These exceptions *really* need to get lost. This gets called outside of draws, which just crashes
// when it tries to propogate the exception back.
const bool clear = (new_width > width || new_height > height);
GSTexture* tex = nullptr;
try
{
@ -3325,6 +3369,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect
delete m_texture;
m_texture = tex;
return true;
}

View File

@ -248,6 +248,7 @@ public:
Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type);
~Target();
void ResizeValidity(const GSVector4i& rect);
void UpdateValidity(const GSVector4i& rect);
void Update(bool reset_age);
@ -404,6 +405,7 @@ public:
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette = false);
Target* FindTargetOverlap(u32 bp, u32 end_block, int type, int psm);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData);
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_w, const int real_h);