diff --git a/bin/resources/GameIndex.yaml b/bin/resources/GameIndex.yaml index 8929aaa5c95da..340d074fb78d5 100644 --- a/bin/resources/GameIndex.yaml +++ b/bin/resources/GameIndex.yaml @@ -18328,6 +18328,11 @@ SLES-52563: SLES-52567: name: "Catwoman" region: "PAL-M7" + gsHWFixes: + recommendedBlendingLevel: 4 # Fixes bloom. + autoFlush: 1 # Fixes ghosting. + textureInsideRT: 1 # Required for offset RTs/textures for post-processing. + roundSprite: 2 # Fixes misaligned post-processing. SLES-52568: name: "Crash Twinsanity" region: "PAL-M5" @@ -60461,6 +60466,11 @@ SLUS-20992: name: "Catwoman" region: "NTSC-U" compat: 5 + gsHWFixes: + recommendedBlendingLevel: 4 # Fixes bloom. + autoFlush: 1 # Fixes ghosting. + textureInsideRT: 1 # Required for offset RTs/textures for post-processing. + roundSprite: 2 # Fixes misaligned post-processing. SLUS-20993: name: "Ghosthunter" region: "NTSC-U" diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 08549831cc0fc..21b0dfb994f79 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -6592,6 +6592,24 @@ void GSRendererHW::ReplaceVerticesWithSprite(const GSVector4i& unscaled_rect, co ReplaceVerticesWithSprite(unscaled_rect, unscaled_rect, unscaled_size, unscaled_rect); } +void GSRendererHW::OffsetDraw(s32 fbp_offset, s32 zbp_offset, s32 xoffset, s32 yoffset) +{ + GL_INS("Offseting render target by %d pages [%x -> %x], Z by %d pages [%x -> %x]", + fbp_offset, m_cached_ctx.FRAME.FBP << 5, zbp_offset, (m_cached_ctx.FRAME.FBP + fbp_offset) << 5); + GL_INS("Offseting vertices by [%d, %d]", xoffset, yoffset); + + m_cached_ctx.FRAME.FBP += fbp_offset; + m_cached_ctx.ZBUF.ZBP += zbp_offset; + + const s32 fp_xoffset = xoffset << 4; + const s32 fp_yoffset = yoffset << 4; + for (u32 i = 0; i < m_vertex.next; i++) + { + m_vertex.buff[i].XYZ.X += fp_xoffset; + m_vertex.buff[i].XYZ.Y += fp_yoffset; + } +} + GSHWDrawConfig& GSRendererHW::BeginHLEHardwareDraw( GSTexture* rt, GSTexture* ds, float rt_scale, GSTexture* tex, float tex_scale, const GSVector4i& unscaled_rect) { diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index 58ddeb3967c3f..f55cf38936f4b 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -217,6 +217,9 @@ class GSRendererHW : public GSRenderer /// Returns true if the specified texture address matches the frame or Z buffer. bool IsTBPFrameOrZ(u32 tbp) const; + /// Offsets the current draw, used for RT-in-RT. Offsets are relative to the *current* FBP, not the new FBP. + void OffsetDraw(s32 fbp_offset, s32 zbp_offset, s32 xoffset, s32 yoffset); + /// Replaces vertices with the specified fullscreen quad. void ReplaceVerticesWithSprite(const GSVector4i& unscaled_rect, const GSVector4i& unscaled_uv_rect, const GSVector2i& unscaled_size, const GSVector4i& scissor); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 315d4dc542561..b61f8a15a97f8 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1541,6 +1541,18 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const dst = t; tex_merge_rt = false; found_t = true; + + // Catwoman offsets the RT as well as the texture. + if (GSRendererHW::GetInstance()->GetCachedCtx()->FRAME.Block() == TEX0.TBP0) + { + // Should be page aligned. + pxAssert(((t->m_TEX0.TBP0 - TEX0.TBP0) % 32) == 0); + const s32 page_offset = (t->m_TEX0.TBP0 - TEX0.TBP0) >> 5; + GL_CACHE("TC: RT also in front of TBP, offsetting draw by %d pages and [%d,%d].", page_offset, x_offset, y_offset); + GSRendererHW::GetInstance()->OffsetDraw(page_offset, page_offset, x_offset, y_offset); + break; + } + if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle) break; else