From 66de28044dc08263da8d007cad73e5a94128b2ef Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Mon, 6 Jan 2025 17:34:30 +0000 Subject: [PATCH] GS/HW: Fixes to texture is target offsets --- pcsx2/GS/GSState.cpp | 2 +- pcsx2/GS/Renderers/HW/GSHwHack.cpp | 6 +- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 89 ++++++++++++++++-------- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 2 +- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index a095abaff0478..2392a5493677b 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -3100,7 +3100,7 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim) { // Pretty confident here... GSVertex* buffer = &m_vertex.buff[0]; - const bool const_spacing = (buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == (m_v.U - m_v.XYZ.X); + const bool const_spacing = std::abs(buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == std::abs(m_v.U - m_v.XYZ.X) && std::abs(buffer[m_index.buff[1]].XYZ.X - buffer[m_index.buff[0]].XYZ.X) < 64; if (const_spacing) return false; diff --git a/pcsx2/GS/Renderers/HW/GSHwHack.cpp b/pcsx2/GS/Renderers/HW/GSHwHack.cpp index 324f8e6449237..7eb88167eeb3e 100644 --- a/pcsx2/GS/Renderers/HW/GSHwHack.cpp +++ b/pcsx2/GS/Renderers/HW/GSHwHack.cpp @@ -1047,7 +1047,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, // compute shadow in RG, // save result in alpha with a TS, // Restore RG channel that we previously copied to render shadows. - + // Important note: The game downsizes the target to half height, then later expands it back up to full size, that's why PCSX2 doesn't like it, we don't support that behaviour. const GIFRegTEX0& Texture = RTEX0; GIFRegTEX0 Frame = {}; @@ -1058,9 +1058,9 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, if ((!rt) || (!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16)) return true; - GL_INS("OI_SonicUnleashed replace draw by a copy"); + GL_INS("OI_SonicUnleashed replace draw by a copy draw %d", r.s_n); - GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget); + GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true); if (!src) return true; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index b0e4a49544221..754f4a10d9e61 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -2570,22 +2570,6 @@ void GSRendererHW::Draw() bool shuffle_target = false; if (!no_rt && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16) { - if (m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0) - { - // FBW is going to be wrong for channel shuffling into a new target, so take it from the source. - FRAME_TEX0.U64 = 0; - FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block(); - FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW; - FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM; - - GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, false, - fm, false, false, false, false, GSVector4i::zero(), true); - - if (tgt) - shuffle_target = tgt->m_32_bits_fmt; - - tgt = nullptr; - } if (!shuffle_target && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16) { const GSVertex* v = &m_vertex.buff[0]; @@ -2604,6 +2588,23 @@ void GSRendererHW::Draw() shuffle_target = shuffle_coords && (draw_width & 7) == 0 && std::abs(draw_width - read_width) <= 1; } + + if (m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 || !shuffle_target) + { + // FBW is going to be wrong for channel shuffling into a new target, so take it from the source. + FRAME_TEX0.U64 = 0; + FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block(); + FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW; + FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM; + + GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, false, + fm, false, false, false, false, GSVector4i::zero(), true); + + if (tgt) + shuffle_target = tgt->m_32_bits_fmt; + + tgt = nullptr; + } } possible_shuffle = !no_rt && (((shuffle_target/*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle()); const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && m_context->ALPHA.C == 0 && m_env.TEXA.AEM; @@ -2829,6 +2830,7 @@ void GSRendererHW::Draw() if (!possible_shuffle && m_split_texture_shuffle_pages == 0) m_r = m_r.rintersect(t_size_rect); + GSVector4i lookup_rect = unclamped_draw_rect; // Do the lookup with the real format on a shuffle, if possible. if (possible_shuffle && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 && GSLocalMemory ::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) { @@ -2842,6 +2844,22 @@ void GSRendererHW::Draw() FRAME_TEX0.PSM = next_ctx.TEX0.PSM; else FRAME_TEX0.PSM = PSMCT32; // Guess full color if no upcoming hint, it'll fix itself later. + + // This is just for overlap detection, it doesn't matter which direction we do this in + if (GSLocalMemory::m_psm[FRAME_TEX0.PSM].bpp == 32) + { + // Shuffling with a double width (Sonic Unleashed for example which does a wierd shuffle/not shuffle green backup/restore). + if (src && std::abs((lookup_rect.width() / 2) - src->m_from_target->m_unscaled_size.x) <= 8) + { + lookup_rect.x /= 2; + lookup_rect.z /= 2; + } + else + { + lookup_rect.y /= 2; + lookup_rect.w /= 2; + } + } } // Normally we would use 1024 here to match the clear above, but The Godfather does a 1023x1023 draw instead @@ -2855,7 +2873,7 @@ void GSRendererHW::Draw() const bool preserve_downscale_draw = scale_draw < 0 || (scale_draw == 0 && ((src && src->m_from_target && src->m_from_target->m_downscaled) || is_possible_mem_clear == ClearType::ClearWithDraw)); rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true, - fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, unclamped_draw_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(), + fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(), GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, (no_ds || !ds) ? -1 : (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0)); // Draw skipped because it was a clear and there was no target. @@ -2894,7 +2912,7 @@ void GSRendererHW::Draw() else if (rt->m_TEX0.TBP0 != m_cached_ctx.FRAME.Block()) { int vertical_offset = ((static_cast(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y; // I know I could just not shift it.. - + int texture_offset = 0; const int horizontal_offset = ((static_cast((m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0)) / 32) % static_cast(std::max(rt->m_TEX0.TBW, 1U))) * frame_psm.pgs.x; // Used to reduce the offset made later in channel shuffles m_target_offset = std::abs(static_cast((m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0)) >> 5); @@ -2905,6 +2923,7 @@ void GSRendererHW::Draw() GSVector2i new_scaled_size = rt->m_unscaled_size * rt->m_scale; // Make sure to use the original format for the offset. int new_offset = std::abs((vertical_offset / frame_psm.pgs.y) * GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.y); + texture_offset = new_offset; new_scaled_size.y += new_offset * rt->m_scale; GSTexture* tex = g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, true); @@ -2915,18 +2934,13 @@ void GSRendererHW::Draw() g_gs_device->StretchRect(rt->m_texture, GSVector4(0,0,1,1), tex, GSVector4(dRect), ShaderConvert::COPY, false); - if (src && src->m_from_target && src->m_from_target == rt) - { - src->m_texture = rt->m_texture; - src->m_target_direct = false; - src->m_shared_texture = false; - } - else + if (src && src->m_from_target && src->m_from_target == rt && src->m_target_direct) { - //m_target_memory_usage -= dst->m_texture->GetMemUsage(); - g_gs_device->Recycle(rt->m_texture); + src->m_texture = tex; } + g_gs_device->Recycle(rt->m_texture); + rt->m_valid.y += new_offset; rt->m_valid.w += new_offset; rt->m_drawn_since_read.y += new_offset; @@ -2957,8 +2971,26 @@ void GSRendererHW::Draw() for (u32 i = 0; i < m_vertex.tail; i++) { - v[i].XYZ.Y += vertical_offset << 4; v[i].XYZ.X += horizontal_offset << 4; + v[i].XYZ.Y += vertical_offset << 4; + } + + if (texture_offset && src && src->m_from_target && src->m_target_direct && src->m_from_target == rt) + { + GSVector4i src_region = src->GetRegionRect(); + + if (src_region.rempty()) + { + src_region = GSVector4i::loadh(rt->m_unscaled_size); + src_region.y += texture_offset; + } + else + { + src_region.y += texture_offset; + src_region.w += texture_offset; + } + src->m_region.SetX(src_region.x, src_region.z); + src->m_region.SetY(src_region.y, src_region.w); } m_context->scissor.in.x += horizontal_offset; @@ -3003,6 +3035,7 @@ void GSRendererHW::Draw() src->m_texture = rt->m_texture; src->m_scale = rt->GetScale(); src->m_unscaled_size = rt->m_unscaled_size; + } target_scale = rt->GetScale(); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 1e452cdb58e21..8ec1411e52eb2 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1915,7 +1915,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe } } // Probably pointing to half way through the target - else if (!min_rect.rempty()&& GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets) + else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets) { // Problem: Project - Snowblind and Tomb Raider offset the RT but not the Z /*if (offset != -1 && (bp - t->m_TEX0.TBP0) != offset)