From 80e50b87f77d6400398273ceb32efa304124063c Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Thu, 16 Jan 2025 01:37:46 +0000 Subject: [PATCH] GS/HW: Fixes for Tex in RT and shuffle detection --- pcsx2/GS/Renderers/HW/GSHwHack.cpp | 3 ++- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 16 +++++++++----- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 28 +++++++----------------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/pcsx2/GS/Renderers/HW/GSHwHack.cpp b/pcsx2/GS/Renderers/HW/GSHwHack.cpp index e751cf003d..7114126577 100644 --- a/pcsx2/GS/Renderers/HW/GSHwHack.cpp +++ b/pcsx2/GS/Renderers/HW/GSHwHack.cpp @@ -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(copy_size.x) / static_cast(src_size.x), static_cast(copy_size.y) / static_cast(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); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index ac6849d316..f933bf6efa 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -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; diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index ad42ec3135..01a585779f 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -336,22 +336,8 @@ 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(); - } + 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. @@ -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())