GS/HW: Fixes for Tex in RT and shuffle detection

This commit is contained in:
refractionpcsx2 2025-01-16 01:37:46 +00:00
parent 4d37a9721f
commit 80e50b87f7
3 changed files with 20 additions and 27 deletions

View File

@ -1080,7 +1080,8 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
// This is kind of a bodge because the game confuses everything since the source is really 16bit and it assumes it's really drawing 16bit on the copy back, resizing the target.
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y * (src->m_32_bits_fmt ? 1 : 2));
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);

View File

@ -2597,14 +2597,16 @@ void GSRendererHW::Draw()
// offset coordinates swap around RG/BA. (Ace Combat)
const u32 minv = m_cached_ctx.CLAMP.MINV;
const u32 minu = m_cached_ctx.CLAMP.MINU;
const bool rgba_shuffle = ((m_cached_ctx.CLAMP.WMS == m_cached_ctx.CLAMP.WMT && m_cached_ctx.CLAMP.WMS == CLAMP_REGION_REPEAT) && (minu && minv));
// Make sure minu or minv are actually a mask on some bits, false positives of games setting 512 (0x1ff) are not masks used for shuffles.
const bool rgba_shuffle = ((m_cached_ctx.CLAMP.WMS == m_cached_ctx.CLAMP.WMT && m_cached_ctx.CLAMP.WMS == CLAMP_REGION_REPEAT) && (minu && minv && ((minu + 1 & minu) || (minv + 1 & minv))));
const bool shuffle_coords = ((first_x ^ first_u) & 0xF) == 8 || rgba_shuffle;
// Round up half of second coord, it can sometimes be slightly under.
const int draw_width = std::abs(v[1].XYZ.X + 9 - v[0].XYZ.X) >> 4;
const int read_width = std::abs(second_u - first_u);
shuffle_target = shuffle_coords && (draw_width & 7) == 0 && std::abs(draw_width - read_width) <= 1;
// m_skip check is just mainly for NFS Undercover, but should hopefully pick up any other games which rewrite shuffles.
shuffle_target = shuffle_coords && (((draw_width & 7) == 0 && std::abs(draw_width - read_width) <= 1) || m_skip > 50);
}
if (!shuffle_target)
@ -2651,7 +2653,7 @@ void GSRendererHW::Draw()
return;
}
possible_shuffle &= src && (src->m_from_target != nullptr);
possible_shuffle &= src && (src->m_from_target != nullptr && (src->m_from_target->m_32_bits_fmt) || (m_skip && possible_shuffle));
// We don't know the alpha range of direct sources when we first tried to optimize the alpha test.
// Moving the texture lookup before the ATST optimization complicates things a lot, so instead,
// recompute it, and everything derived from it again if it changes.
@ -2693,7 +2695,7 @@ void GSRendererHW::Draw()
// Urban Reign trolls by scissoring a draw to a target at 0x0-0x117F to 378x449 which ends up the size being rounded up to 640x480
// causing the buffer to expand to around 0x1400, which makes a later framebuffer at 0x1180 to fail to be created correctly.
// We can cheese this by checking if the Z is masked and the resultant colour is going to be black anyway.
const bool output_black = PRIM->ABE && ((m_context->ALPHA.A == 1 || m_context->ALPHA.IsBlack()) && m_context->ALPHA.D != 1) && m_draw_env->COLCLAMP.CLAMP == 1;
const bool output_black = PRIM->ABE && ((m_context->ALPHA.A == 1 && m_context->ALPHA.D > 1) || (m_context->ALPHA.IsBlack() && m_context->ALPHA.D != 1)) && m_draw_env->COLCLAMP.CLAMP == 1;
const bool can_expand = !(m_cached_ctx.ZBUF.ZMSK && output_black);
// Estimate size based on the scissor rectangle and height cache.
@ -7662,7 +7664,9 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* tex, const GSVector4i& r_draw)
{
/*if (r_draw.w > 1024 && (m_vt.m_primclass == GS_SPRITE_CLASS) && (m_vertex.next == 2) && m_process_texture && !PRIM->ABE && tex && !tex->m_target && m_cached_ctx.TEX0.TBW > 0)
// Not required when using Tex in RT
if (r_draw.w > 1024 && (m_vt.m_primclass == GS_SPRITE_CLASS) && (m_vertex.next == 2) && m_process_texture && !PRIM->ABE &&
tex && !tex->m_target && m_cached_ctx.TEX0.TBW > 0 && GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled)
{
GL_PUSH("OI_BlitFMV");
@ -7716,7 +7720,7 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
g_texture_cache->InvalidateVideoMemSubTarget(_rt);
return false; // skip current draw
}*/
}
// Nothing to see keep going
return true;

View File

@ -335,24 +335,10 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
{
// Results won't be square, if it's not invalidation, it's a texture, which is problematic to translate, so let's not (FIFA 2005).
if (!is_invalidation)
{
if (sbp != tbp)
{
// Just take the start page, as this is likely tex in rt, and that's all we care about.
const u32 start_page = (in_rect.y / src_page_size.y) + (in_rect.x / src_page_size.x);
in_rect.x = (start_page % dst_pgw) * dst_page_size.x;
in_rect.y = (start_page / dst_pgw) * dst_page_size.y;
in_rect.z = in_rect.x + dst_page_size.x;
in_rect.w = in_rect.y + dst_page_size.y;
return in_rect;
}
else
{
DevCon.Warning("Uneven pages mess up sbp %x dbp %x spgw %d dpgw %d", sbp, tbp, src_pgw, dst_pgw);
return GSVector4i::zero();
}
}
//TODO: Maybe control dirty blocks directly and add them page at a time for better granularity.
const u32 start_y_page = (rect_pages.y * src_pgw) / dst_pgw;
@ -1146,7 +1132,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// Try to make sure the target has available what we need, be careful of self referencing frames with font in the alpha.
// Also is we have already found a target which we had to offset in to by using a region or exact address,
// it's probable that's more correct than being inside (Tomb Raider Legends + Project Snowblind)
if (!overlaps || (found_t && dst->m_TEX0.TBP0 >= bp && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)))
// Vakyrie Profile 2 also has some in draws which get done on a different target due to a slight offset, so we need to make sure we have the newer one.
if (!overlaps || (found_t && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)))
continue;
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
@ -1487,8 +1474,9 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
DevCon.Warning("BP %x - 16bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
continue;
}
else if (!possible_shuffle && (GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 && bw != 1 &&
!((t->m_TEX0.TBW == (bw / 2)) || (t->m_TEX0.TBW >= (bw / 2) && (block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y)))))
// Keep note that 2 bw is basically 1 normal page, as bw is in 64 pixels, and 8bit pages are 128 pixels wide, aka 2 bw.
else if (!possible_shuffle && (GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 &&
!((t->m_TEX0.TBW == (bw / 2)) || (((bw + 1) / 2) <= t->m_TEX0.TBW && (block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y)))))
{
DevCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
continue;
@ -1566,7 +1554,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
//rect = rect.rintersect(t->m_valid);
if (rect.rempty())
if (rect.rintersect(t->m_valid).rempty())
continue;
if (!t->m_dirty.empty())