From b568e1387a160e23d97e30deb9e807668599d9bc Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Sun, 26 Jan 2025 16:35:49 +0000 Subject: [PATCH] GS/HW: Allow offsetting in to a target if full contained. --- pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 0c4919759f..4cf94b8632 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1189,7 +1189,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const const u32* const clut = g_gs_renderer->m_mem.m_clut; GSTexture* const gpu_clut = (psm_s.pal > 0) ? g_gs_renderer->m_mem.m_clut.GetGPUTexture() : nullptr; - const SourceRegion region = SourceRegion::Create(TEX0, CLAMP); + SourceRegion region = SourceRegion::Create(TEX0, CLAMP); // Prevent everything going to rubbish if a game somehow sends a TW/TH above 10, and region isn't being used. if ((TEX0.TW > 10 && !region.HasX()) || (TEX0.TH > 10 && !region.HasY())) @@ -1754,6 +1754,13 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const } else { + + GSVector4i inside_block_boundary_rect = r; + inside_block_boundary_rect.x = (inside_block_boundary_rect.x + (psm_s.bs.x - 1)) & ~(psm_s.bs.x - 1); + inside_block_boundary_rect.y = (inside_block_boundary_rect.y + (psm_s.bs.y - 1)) & ~(psm_s.bs.y - 1); + // Round up to the nearst block boundary for lookup to avoid problems due to bilinear and inclusive rects. + inside_block_boundary_rect.z = inside_block_boundary_rect.z & ~(psm_s.bs.x - 1); + inside_block_boundary_rect.w = inside_block_boundary_rect.w & ~(psm_s.bs.y - 1); // Some games, such as Tomb Raider: Underworld, and Destroy All Humans shift the texture pointer // back behind the framebuffer, but then offset their texture coordinates to compensate. Why they // do this, I have no idea... but it's usually only a page wide/high of an offset. Thankfully, @@ -1817,6 +1824,29 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const else continue; } + // Else read it back, might be our only choice. Ridge Racer writes to the right side of 0x1a40 for headlights, then tries to access it with the base of 0x9a0 + // naturally, it misses here. But let's make sure the formats match well enough. coordinates could be too low by half a pixel when on the edge of the screen, so need a conservitive rect. + else if (bw == t->m_TEX0.TBW && GSLocalMemory::m_psm[psm].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp && t->Inside(bp, bw, psm, inside_block_boundary_rect)) + { + if (!t->HasValidBitsForFormat(psm, req_color, req_alpha, true)) + continue; + + GIFRegCLAMP fake_CLAMP; + fake_CLAMP.WMS = CLAMP_REGION_CLAMP; + fake_CLAMP.WMT = CLAMP_REGION_CLAMP; + fake_CLAMP.MINU = 0; + fake_CLAMP.MINV = 0; + fake_CLAMP.MAXV = std::min(static_cast(1u << TEX0.TH), 1022u); + fake_CLAMP.MAXU = std::min(static_cast(1u << TEX0.TW), 1022u); + region = SourceRegion::Create(TEX0, fake_CLAMP); + + const GSVector4i custom_offset_rect = TranslateAlignedRectByPage(t, bp, psm, bw, block_boundary_rect); + x_offset = custom_offset_rect.x; + y_offset = custom_offset_rect.y; + dst = t; + tex_merge_rt = false; + found_t = true; + } } } }