GS/HW: Fix some back to back shuffles and inside source invalidation

This commit is contained in:
refractionpcsx2 2025-01-14 22:51:30 +00:00
parent e9341bde7c
commit 8de5f53252
4 changed files with 29 additions and 22 deletions

View File

@ -936,8 +936,8 @@ GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex)
// Round up the page as channel shuffles are generally done in pages at a time
// Keep in mind the source might be an 8bit texture
int src_width = tex->GetUnscaledWidth();
int src_height = tex->GetUnscaledHeight();
int src_width = tex->m_from_target ? tex->m_from_target->m_valid.width() : tex->GetUnscaledWidth();
int src_height = tex->m_from_target ? tex->m_from_target->m_valid.height() : tex->GetUnscaledHeight();
if (!tex->m_from_target && GSLocalMemory::m_psm[tex->m_TEX0.PSM].bpp == 8)
{
@ -2053,9 +2053,7 @@ void GSRendererHW::Draw()
DumpVertices(s);
}
#ifdef ENABLE_OGL_DEBUG
static u32 num_skipped_channel_shuffle_draws = 0;
#endif
// We mess with this state as an optimization, so take a copy and use that instead.
const GSDrawingContext* context = m_context;
@ -2079,24 +2077,26 @@ void GSRendererHW::Draw()
// Tomb Raider: Underworld does similar, except with R, G, B in separate palettes, therefore
// we need to split on those too.
m_channel_shuffle = IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && m_last_channel_shuffle_end_block > m_context->FRAME.Block();
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && m_last_channel_shuffle_end_block > m_context->FRAME.Block() &&
m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0;
#ifdef ENABLE_OGL_DEBUG
if (m_channel_shuffle)
{
m_last_channel_shuffle_fbp = m_context->FRAME.Block();
m_last_channel_shuffle_tbp = m_context->TEX0.TBP0;
num_skipped_channel_shuffle_draws++;
return;
}
#ifdef ENABLE_OGL_DEBUG
if (num_skipped_channel_shuffle_draws > 0)
GL_INS("Skipped %u channel shuffle draws", num_skipped_channel_shuffle_draws);
GL_CACHE("Skipped %d channel shuffle draws ending at %d", num_skipped_channel_shuffle_draws, s_n);
#endif
num_skipped_channel_shuffle_draws = 0;
m_last_channel_shuffle_fbp = 0xffff;
m_last_channel_shuffle_tbp = 0xffff;
m_last_channel_shuffle_end_block = 0xffff;
#else
if (m_channel_shuffle)
return;
#endif
}
GL_PUSH("HW Draw %d (Context %u)", s_n, PRIM->CTXT);
@ -3070,6 +3070,7 @@ void GSRendererHW::Draw()
if (m_channel_shuffle)
{
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
// If it's a new target, we don't know where the end is as it's starting on a shuffle, so just do every shuffle following.
m_last_channel_shuffle_end_block = (rt->m_last_draw >= s_n) ? (MAX_BLOCKS - 1) : (rt->m_end_block < rt->m_TEX0.TBP0 ? (rt->m_end_block + MAX_BLOCKS) : rt->m_end_block);
@ -3211,6 +3212,7 @@ void GSRendererHW::Draw()
if (rt)
{
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
// Urban Chaos goes from Z16 to C32, so let's just use the rt's original end block.
if (!src->m_from_target || GSLocalMemory::m_psm[src->m_from_target_TEX0.PSM].bpp != GSLocalMemory::m_psm[rt->m_TEX0.PSM].bpp)
m_last_channel_shuffle_end_block = rt->m_end_block;
@ -5620,8 +5622,9 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
bool& target_region, GSVector2i& unscaled_size, float& scale, GSDevice::RecycledTexture& src_copy)
{
const int tex_diff = tex->m_from_target ? static_cast<int>(m_cached_ctx.TEX0.TBP0 - tex->m_from_target->m_TEX0.TBP0) : 0;
const int tex_diff = tex->m_from_target ? static_cast<int>(m_cached_ctx.TEX0.TBP0 - tex->m_from_target->m_TEX0.TBP0) : static_cast<int>(m_cached_ctx.TEX0.TBP0 - tex->m_TEX0.TBP0);
const int frame_diff = rt ? static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) : 0;
// Detect framebuffer read that will need special handling
const GSTextureCache::Target* src_target = nullptr;
if (m_conf.tex == m_conf.rt && !(m_channel_shuffle && tex && (tex_diff != frame_diff || target_region)))

View File

@ -174,6 +174,7 @@ private:
u32 m_last_channel_shuffle_fbmsk = 0;
u32 m_last_channel_shuffle_fbp = 0;
u32 m_last_channel_shuffle_tbp = 0;
u32 m_last_channel_shuffle_end_block = 0;
GIFRegFRAME m_split_clear_start = {};

View File

@ -2542,8 +2542,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
// Avoid making garbage targets (usually PCRTC).
if (GSVector4i::loadh(size).rempty())
return nullptr;
if (TEX0.TBP0 == 0x3320 || TEX0.TBP0 == 0x32a0)
DevCon.Warning("Making target %x on draw %d", TEX0.TBP0, GSState::s_n);
Target* dst = Target::Create(TEX0, size.x, size.y, scale, type, true);
if (!dst) [[unlikely]]
return nullptr;
@ -3428,6 +3427,12 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
const u32 bw = off.bw();
const u32 psm = off.psm();
// Get the bounds that we're invalidating in blocks, so we can remove any targets which are completely contained.
// Unfortunately sometimes the draw rect is incorrect, and since the end block gets the rect -1, it'll underflow,
// so we need to prevent that from happening. Just make it a single block in that case, and hope for the best.
const u32 start_bp = GSLocalMemory::GetStartBlockAddress(off.bp(), off.bw(), off.psm(), rect);
const u32 end_bp = rect.rempty() ? start_bp : GSLocalMemory::GetUnwrappedEndBlockAddress(off.bp(), off.bw(), off.psm(), rect);
if (!target)
{
// Remove Source that have same BP as the render target (color&dss)
@ -3438,7 +3443,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
Source* s = *i;
++i;
if (GSUtil::HasSharedBits(bp, psm, s->m_TEX0.TBP0, s->m_TEX0.PSM) ||
if ((GSUtil::HasSharedBits(psm, s->m_TEX0.PSM) && (bp >= start_bp && bp < end_bp)) ||
(GSUtil::HasSharedBits(bp, psm, s->m_from_target_TEX0.TBP0, s->m_TEX0.PSM) && s->m_target))
{
m_src.RemoveAt(s);
@ -3535,11 +3540,6 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
if (!target)
return;
// Get the bounds that we're invalidating in blocks, so we can remove any targets which are completely contained.
// Unfortunately sometimes the draw rect is incorrect, and since the end block gets the rect -1, it'll underflow,
// so we need to prevent that from happening. Just make it a single block in that case, and hope for the best.
const u32 start_bp = GSLocalMemory::GetStartBlockAddress(off.bp(), off.bw(), off.psm(), rect);
const u32 end_bp = rect.rempty() ? start_bp : GSLocalMemory::GetUnwrappedEndBlockAddress(off.bp(), off.bw(), off.psm(), rect);
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(psm);
@ -4819,6 +4819,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_texture = dst->m_texture;
src->m_unscaled_size = dst->m_unscaled_size;
src->m_shared_texture = true;
if(channel_shuffle)
m_temporary_source = src;
}
// Invalidate immediately on recursive draws, because if we don't here, InvalidateVideoMem() will.
@ -5074,7 +5077,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
}
// kill source immediately if it's the RT/DS, because that'll get invalidated immediately
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(dst->m_TEX0.TBP0))
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(dst->m_TEX0.TBP0) || channel_shuffle)
{
GL_CACHE("TC: Source is RT or ZBUF, invalidating after draw.");
m_temporary_source = src;

View File

@ -518,7 +518,7 @@ public:
/// Removes any sources which point to the specified target.
void InvalidateSourcesFromTarget(const Target* t);
/// Replaces a source's texture externally. Required for some CRC hacks.
/// Removes any sources which point to the same address as a new target.
void ReplaceSourceTexture(Source* s, GSTexture* new_texture, float new_scale, const GSVector2i& new_unscaled_size,
HashCacheEntry* hc_entry, bool new_texture_is_shared);