GS-HW: Improve clear detection and avoid making bad render targets

This commit is contained in:
refractionpcsx2 2023-02-22 16:55:32 +00:00
parent 6013d7172a
commit 264086e0aa
3 changed files with 40 additions and 27 deletions

View File

@ -756,7 +756,7 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
}
}
u32 width = m_context->FRAME.FBW * 64u;
u32 width = std::min(m_context->FRAME.FBW * 64u, static_cast<u32>(m_context->scissor.in.z));
// If it's a channel shuffle, it'll likely be just a single page, so assume full screen.
if (m_channel_shuffle)
@ -1573,7 +1573,17 @@ void GSRendererHW::Draw()
GSTextureCache::Target* rt = nullptr;
if (!no_rt)
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm, false, unscaled_target_size.x, unscaled_target_size.y, force_preload);
{
const bool is_square = (unscaled_target_size.y == unscaled_target_size.x) && m_r.w >= 1024 && m_vertex.next;
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm, false, unscaled_target_size.x, unscaled_target_size.y, force_preload, IsConstantDirectWriteMemClear(false) && is_square);
// Draw skipped because it was a clear and there was no target.
if (!rt)
{
OI_GsMemClear();
return;
}
}
TEX0.TBP0 = context->ZBUF.Block();
TEX0.TBW = context->FRAME.FBW;
@ -1581,7 +1591,9 @@ void GSRendererHW::Draw()
GSTextureCache::Target* ds = nullptr;
if (!no_ds)
{
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite(), 0, false, unscaled_target_size.x, unscaled_target_size.y, force_preload);
}
if (process_texture)
{
@ -1739,6 +1751,7 @@ void GSRendererHW::Draw()
const bool is_mem_clear = IsConstantDirectWriteMemClear(false);
const bool can_update_size = !is_mem_clear && !m_texture_shuffle && !m_channel_shuffle;
const GSVector2i resolution = PCRTCDisplays.GetResolution();
{
// We still need to make sure the dimensions of the targets match.
const GSVector2 up_s(GetTextureScaleFactor());
@ -1753,23 +1766,20 @@ void GSRendererHW::Draw()
pxAssert(rt->m_texture->GetScale() == up_s);
rt->ResizeTexture(new_w, new_h, up_s);
const GSVector2i tex_size = rt->m_texture->GetSize();
// Avoid temporary format changes, as this will change the end block and could break things.
if ((old_height != tex_size.y) && can_update_size)
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector2 tex_scale = rt->m_texture->GetScale();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / tex_scale.x, tex_size.y / tex_scale.y);
const GSVector2i tex_size = rt->m_texture->GetSize();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / up_s.x, tex_size.y / up_s.y);
rt->ResizeValidity(new_valid);
}
GSVector2i resolution = PCRTCDisplays.GetResolution();
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
// 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)
if (old_end_block != rt->m_TEX0.TBP0 && 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);
@ -1794,20 +1804,17 @@ void GSRendererHW::Draw()
pxAssert(ds->m_texture->GetScale() == up_s);
ds->ResizeTexture(new_w, new_h, up_s);
const GSVector2i tex_size = ds->m_texture->GetSize();
if ((old_height != tex_size.y) && can_update_size)
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector2 tex_scale = ds->m_texture->GetScale();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / tex_scale.x, tex_size.y / tex_scale.y);
const GSVector2i tex_size = ds->m_texture->GetSize();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / up_s.x, tex_size.y / up_s.y);
ds->ResizeValidity(new_valid);
}
GSVector2i resolution = PCRTCDisplays.GetResolution();
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
if (new_height && old_end_block != ds->m_end_block)
if (old_end_block != ds->m_TEX0.TBP0 && 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);
@ -1955,13 +1962,13 @@ void GSRendererHW::Draw()
// Temporary source *must* be invalidated before normal, because otherwise it'll be double freed.
m_tc->InvalidateTemporarySource();
// If it's a mem clear or shuffle we don't want to resize the texture, it can cause textures to take up the entire
// video memory, and that is not good.
//
if ((fm & fm_mask) != fm_mask && rt)
{
//rt->m_valid = rt->m_valid.runion(r);
if(can_update_size)
rt->UpdateValidity(m_r);
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidBits(~fm & fm_mask);
@ -1973,9 +1980,8 @@ void GSRendererHW::Draw()
if (zm != 0xffffffff && ds)
{
//ds->m_valid = ds->m_valid.runion(r);
// Shouldn't be a problem as Z will be masked.
if (can_update_size)
ds->UpdateValidity(m_r);
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
ds->UpdateValidBits(GSLocalMemory::m_psm[context->ZBUF.PSM].fmsk);

View File

@ -599,7 +599,7 @@ GSTextureCache::Target* GSTextureCache::FindTargetOverlap(u32 bp, u32 end_block,
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)
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, bool is_clear)
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const GSVector2& new_s = static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor();
@ -806,6 +806,13 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
if (!dst)
{
// Skip full screen clears from making massive targets.
if (is_clear)
{
GL_CACHE("TC: Create RT skipped on clear draw");
return nullptr;
}
GL_CACHE("TC: Lookup %s(%s) %dx%d, miss (0x%x, %s)", is_frame ? "Frame" : "Target", to_string(type), size.x, size.y, bp, psm_str(TEX0.PSM));
dst = CreateTarget(TEX0, size.x, size.y, type, true);
@ -3317,8 +3324,8 @@ void GSTextureCache::Target::ResizeValidity(const GSVector4i& rect)
}
else
{
m_valid = rect;
m_drawn_since_read = rect;
// No valid size, so need to resize down.
return;
}
// 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

View File

@ -416,7 +416,7 @@ public:
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* 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, bool is_clear = false);
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_w, const int real_h);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.