GS/HW/TC: Force a temporary source creation in edges cases.

Case: When looking up a source, we find a perfect BP hit for a target.
However, the requested area is outside the target's valid area.
Don't use the target direct and instead load from memory in a temporary source.

Co-authored-by: refraction
This commit is contained in:
TJnotJT 2025-11-02 19:31:54 -05:00 committed by lightningterror
parent b5a2d04b2e
commit 44f47f11b8
2 changed files with 20 additions and 6 deletions

View File

@ -1347,6 +1347,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
int x_offset = 0; int x_offset = 0;
int y_offset = 0; int y_offset = 0;
// Indicates that in looking for targets that match the source BP, we found a perfect BP match
// but the target's valid area was outside the required area for the source. In such cases
// we want to create a temporary source and load the data from memory.
bool target_bp_hit_outside_valid_area = false;
#ifdef DISABLE_HW_TEXTURE_CACHE #ifdef DISABLE_HW_TEXTURE_CACHE
if (0) if (0)
#else #else
@ -1725,6 +1730,15 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (!possible_shuffle && outside_target) if (!possible_shuffle && outside_target)
{ {
// Hit a target but source required area outside the target's valid area.
target_bp_hit_outside_valid_area = true;
GL_CACHE(
"TC: LookupSource: Target BP match but outside valid area;"
" Source=(BP=%04x, BW=%d, PSM=%s, req=(%x,%x - %x,%x));"
" Target=(BP=%04x, BW=%d, PSM=%s, valid_area=(%x,%x - %x,%x))",
bp, bw, GSUtil::GetPSMName(psm), r.x, r.y, r.z, r.w,
t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM),
t->m_valid.x, t->m_valid.y, t->m_valid.z, t->m_valid.w);
continue; continue;
} }
else else
@ -2034,7 +2048,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// //
// Sigh... They don't help us. // Sigh... They don't help us.
if (!found_t && !dst && !GSConfig.UserHacks_DisableDepthSupport) if (!found_t && !dst && !GSConfig.UserHacks_DisableDepthSupport && !target_bp_hit_outside_valid_area)
{ {
// Let's try a trick to avoid to use wrongly a depth buffer // Let's try a trick to avoid to use wrongly a depth buffer
// Unfortunately, I don't have any Arc the Lad testcase // Unfortunately, I don't have any Arc the Lad testcase
@ -2172,7 +2186,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
} }
} }
src = CreateSource(src_TEX0, TEXA, dst, x_offset, y_offset, lod, &rect, gpu_clut, region); src = CreateSource(src_TEX0, TEXA, dst, x_offset, y_offset, lod, &rect, gpu_clut, region, target_bp_hit_outside_valid_area);
if (!src) [[unlikely]] if (!src) [[unlikely]]
return nullptr; return nullptr;
} }
@ -5773,7 +5787,7 @@ void GSTextureCache::IncAge()
} }
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work. //Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region) GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool target_bp_hit)
{ {
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
Source* src = new Source(TEX0, TEXA); Source* src = new Source(TEX0, TEXA);
@ -6239,7 +6253,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
} }
else else
{ {
if (GSUtil::GetChannelMask(TEX0.PSM) == 0xf && TEX0.TBP0 != GSRendererHW::GetInstance()->GetCachedCtx()->FRAME.Block() && TEX0.TBP0 != GSRendererHW::GetInstance()->GetCachedCtx()->ZBUF.Block()) if (GSUtil::GetChannelMask(TEX0.PSM) == 0xf && TEX0.TBP0 != GSRendererHW::GetInstance()->GetCachedCtx()->FRAME.Block() && TEX0.TBP0 != GSRendererHW::GetInstance()->GetCachedCtx()->ZBUF.Block() && !target_bp_hit)
{ {
// Kill any possible targets we missed, they might be wrong now. // Kill any possible targets we missed, they might be wrong now.
g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, TEX0.TBP0, TEX0.PSM, GSRendererHW::GetInstance()->GetCachedCtx()->FRAME.FBMSK, true); g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, TEX0.TBP0, TEX0.PSM, GSRendererHW::GetInstance()->GetCachedCtx()->FRAME.FBMSK, true);
@ -6247,7 +6261,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
} }
// kill source immediately after the draw if it's the RT, because that'll get invalidated immediately. // kill source immediately after the draw if it's the RT, because that'll get invalidated immediately.
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(TEX0.TBP0, true) && GSRendererHW::GetInstance()->ChannelsSharedTEX0FRAME()) if (target_bp_hit || (GSRendererHW::GetInstance()->IsTBPFrameOrZ(TEX0.TBP0, true) && GSRendererHW::GetInstance()->ChannelsSharedTEX0FRAME()))
{ {
GL_CACHE("TC: Source == RT before RT creation, invalidating after draw."); GL_CACHE("TC: Source == RT before RT creation, invalidating after draw.");
m_temporary_source = src; m_temporary_source = src;

View File

@ -445,7 +445,7 @@ protected:
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture; std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
std::unique_ptr<GSDownloadTexture> m_uint32_download_texture; std::unique_ptr<GSDownloadTexture> m_uint32_download_texture;
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region); Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false);
bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame, bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr); bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);