diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index b7ede7e6a5..82312983da 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -288,7 +288,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con bool found_t = false; for (auto t : m_dst[RenderTarget]) { - if (t->m_used && t->m_dirty.empty()) + const bool t_clean = t->m_dirty.empty(); + if (t->m_used) { // Typical bug (MGS3 blue cloud): // 1/ RT used as 32 bits => alpha channel written @@ -299,7 +300,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con // Solution: consider the RT as 32 bits if the alpha was used in the past u32 t_psm = (t->m_dirty_alpha) ? t->m_TEX0.PSM & ~0x1 : t->m_TEX0.PSM; - if (GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0, t_psm)) + if (t_clean && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0, t_psm)) { // It is a complex to convert the code in shader. As a reference, let's do it on the CPU, it will be slow but // 1/ it just works :) @@ -316,7 +317,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con found_t = true; break; } - else if ((t->m_TEX0.TBW >= 16) && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0 + t->m_TEX0.TBW * 0x10, t->m_TEX0.PSM)) + else if (t_clean && (t->m_TEX0.TBW >= 16) && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0 + t->m_TEX0.TBW * 0x10, t->m_TEX0.PSM)) { // Detect half of the render target (fix snow engine game) // Target Page (8KB) have always a width of 64 pixels @@ -678,6 +679,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, int } else { + dst->m_TEX0.TBW = TEX0.TBW; dst->Update(); } @@ -877,7 +879,6 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r GL_CACHE("TC: Dirty Target(%s) %d (0x%x) r(%d,%d,%d,%d)", to_string(type), t->m_texture ? t->m_texture->GetID() : 0, t->m_TEX0.TBP0, r.x, r.y, r.z, r.w); - t->m_TEX0.TBW = bw; t->m_dirty.push_back(GSDirtyRect(r, psm, bw)); } else @@ -917,7 +918,6 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r t->m_texture ? t->m_texture->GetID() : 0, t->m_TEX0.TBP0); // TODO: do not add this rect above too - t->m_TEX0.TBW = bw; t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top - y, r.right, r.bottom - y), psm, bw)); continue; } @@ -945,7 +945,6 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r t->m_TEX0.TBP0, t->m_end_block, r.left, r.top + y, r.right, r.bottom + y, bw); - t->m_TEX0.TBW = bw; t->m_dirty.push_back(GSDirtyRect(GSVector4i(r.left, r.top + y, r.right, r.bottom + y), psm, bw)); continue; } @@ -2422,6 +2421,26 @@ GSTextureCache::SurfaceOffset GSTextureCache::ComputeSurfaceOffset(const uint32_ sok.elems[1].psm = t->m_TEX0.PSM; sok.elems[1].rect = t->m_valid; const SurfaceOffset so = ComputeSurfaceOffset(sok); + // Check if any dirty rect in the target overlaps with the offset. + if (so.is_valid && !t->m_dirty.empty()) + { + const SurfaceOffsetKeyElem& t_sok = sok.elems[1]; + const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t_sok.psm]; + const u32 so_bp = t_psm_s.info.bn(so.b2a_offset.x, so.b2a_offset.y, t_sok.bp, t_sok.bw); + const u32 so_bp_end = t_psm_s.info.bn(so.b2a_offset.z - 1, so.b2a_offset.w - 1, t_sok.bp, t_sok.bw); + for (const auto& dr : t->m_dirty) + { + const GSLocalMemory::psm_t& dr_psm_s = GSLocalMemory::m_psm[dr.psm]; + const u32 dr_bp = dr_psm_s.info.bn(dr.r.x, dr.r.y, t_sok.bp, dr.bw); + const u32 dr_bp_end = dr_psm_s.info.bn(dr.r.z - 1, dr.r.w - 1, t_sok.bp, dr.bw); + const bool overlap = GSTextureCache::CheckOverlap(dr_bp, dr_bp_end, so_bp, so_bp_end); + if (overlap) + { + // Dirty rectangle in target overlaps with the found offset. + return { false }; + } + } + } return so; }