From afc0c2581dda0934f5edeae2f729a9fd5952d2ef Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Fri, 17 Jan 2025 01:32:58 +0000 Subject: [PATCH] GS/HW: Further fixes to HW renderer behaviour --- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 44 ++++++++++++++++++++---- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 4 +-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 59bf54f40a06c..4669a6e04a10e 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -735,7 +735,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba, if (m_context->scissor.in.x & 8) { - m_context->scissor.in.x &= ~0xf;//m_vt.m_min.p.x; + m_context->scissor.in.x &= ~0xf; //m_vt.m_min.p.x; if (half_right_vert) m_context->scissor.in.x /= 2; @@ -765,6 +765,38 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba, m_vt.m_min.t.x /= 2.0f; m_vt.m_max.t.x = (m_vt.m_max.t.x + 1.9f) / 2.0f; } + + // Special case used in Call of Duty - World at War where it doubles the height and halves the width, but the height is double doubled. + // Check the height of the original texture, if it's half of the draw height, then make it wide instead. + if (half_bottom_uv && tex->m_from_target && m_cached_ctx.TEX0.TBW == m_cached_ctx.FRAME.FBW && + tex->m_from_target->m_TEX0.TBW == (m_cached_ctx.TEX0.TBW * 2) && (m_cached_ctx.TEX0.TBW * 64) == floor(m_vt.m_max.t.x)) + { + m_r.z *= 2; + m_r.w /= 2; + + m_vt.m_max.t.y /= 2; + m_vt.m_max.t.x *= 2; + m_vt.m_max.p.y /= 2; + m_vt.m_max.p.x *= 2; + m_context->scissor.in.w /= 2; + m_context->scissor.in.z *= 2; + + v[1].XYZ.X = ((v[m_index.buff[m_index.tail - 1]].XYZ.X - m_context->XYOFFSET.OFX) * 2) + m_context->XYOFFSET.OFX; + v[1].XYZ.Y = ((v[m_index.buff[m_index.tail - 1]].XYZ.Y - m_context->XYOFFSET.OFY) / 2) + m_context->XYOFFSET.OFY; + + v[1].U = v[m_index.buff[m_index.tail - 1]].U * 2; + v[1].V = v[m_index.buff[m_index.tail - 1]].V / 2; + + v[1].ST.S = v[m_index.buff[m_index.tail - 1]].ST.S * 2; + v[1].ST.T = v[m_index.buff[m_index.tail - 1]].ST.T / 2; + + m_vertex.head = m_vertex.tail = m_vertex.next = 2; + m_index.tail = 2; + + m_cached_ctx.TEX0.TBW *= 2; + m_cached_ctx.FRAME.FBW *= 2; + GL_CACHE("Half width/double height shuffle detected, width changed to %d", m_cached_ctx.FRAME.FBW); + } } GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex) @@ -2923,8 +2955,8 @@ void GSRendererHW::Draw() FRAME_TEX0.TBW = src->m_from_target->m_TEX0.TBW; } - rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), (scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale, GSTextureCache::RenderTarget, true, - fm, false, force_preload, preserve_rt_color | possible_shuffle, m_r, src); + rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), (scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale, + GSTextureCache::RenderTarget, true, fm, false, force_preload, preserve_rt_color | possible_shuffle, lookup_rect, src); if (!rt) [[unlikely]] { GL_INS("ERROR: Failed to create FRAME target, skipping."); @@ -4453,7 +4485,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM]; const u32 frame_page_offset = std::max(static_cast(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * src->m_TEX0.TBW) - m_target_offset), 0); m_r = GSVector4i(m_r.x & ~(frame_psm.pgs.x - 1), m_r.y & ~(frame_psm.pgs.y - 1), (m_r.z + (frame_psm.pgs.x - 1)) & ~(frame_psm.pgs.x - 1), (m_r.w + (frame_psm.pgs.y - 1)) & ~(frame_psm.pgs.y - 1)); - m_cached_ctx.FRAME.FBP += frame_page_offset; + //m_cached_ctx.FRAME.FBP += frame_page_offset; m_in_target_draw |= frame_page_offset > 0; GSVertex* s = &m_vertex.buff[0]; s[0].XYZ.X = static_cast(m_context->XYOFFSET.OFX + (m_r.x << 4)); @@ -4463,7 +4495,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM]; const u32 tex_page_offset = (m_vt.m_min.t.x / tex_psm.pgs.x) + (m_vt.m_min.t.y / tex_psm.pgs.y); - m_cached_ctx.TEX0.TBP0 += tex_page_offset << 5; + //m_cached_ctx.TEX0.TBP0 += tex_page_offset << 5; s[0].U = m_r.x << 4; s[1].U = m_r.z << 4; s[0].V = m_r.y << 4; @@ -5707,7 +5739,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c target_region = false; source_region.bits = 0; //copied_rt = tex->m_from_target != nullptr; - if (page_offset && m_in_target_draw) + if (m_in_target_draw && (page_offset || frame_diff)) { copy_size.x = m_r.width(); copy_size.y = m_r.height(); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 43c53657ae7cf..72e4d41bd8ca4 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1948,10 +1948,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe (static_cast(min_rect.width()) <= (widthpage_offset * 64))));*/ const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0); const bool no_target_or_newer = (!dst || ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw))); - const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1); + const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)); // if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems. // This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now. - const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32))); + const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32))); if (no_target_or_newer && is_aligned_ok && width_match && overlaps) { const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];