diff --git a/bin/resources/GameIndex.yaml b/bin/resources/GameIndex.yaml index 8b615baae9..2636ee29ec 100644 --- a/bin/resources/GameIndex.yaml +++ b/bin/resources/GameIndex.yaml @@ -383,6 +383,7 @@ PCPX-96322: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. PCPX-96328: name: "3 Title Special Disc (Saru! Get You! 2 - PoPoLoCrois: Hajimari no Bouken - Boku no Natsuyasumi 2)" region: "NTSC-J" @@ -959,6 +960,7 @@ SCAJ-20099: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCAJ-20100: name: "Tenchu Kurenai" region: "NTSC-Ch-J" @@ -1711,6 +1713,7 @@ SCCS-40005: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCCS-40006: name: "Zhen Sanguo Wushuang 2" region: "NTSC-C" @@ -2030,6 +2033,7 @@ SCED-50844: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCED-50907: name: "Final Fantasy X [Bonus Disc - Beyond Final Fantasy]" region: "PAL-Unk" @@ -3387,6 +3391,7 @@ SCES-50760: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCES-50781: name: "Destruction Derby Arenas [Beta, Promo, & Full Retail]" region: "PAL-M6" @@ -5325,6 +5330,7 @@ SCKA-20028: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCKA-20029: name: "Gran Turismo - Concept 2002 Tokyo-Seoul [PlayStation 2 Big Hit Series]" region: "NTSC-K" @@ -5847,6 +5853,7 @@ SCPS-11003: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCPS-11004: name: "Bikkuri Mouse" region: "NTSC-J" @@ -6688,6 +6695,7 @@ SCPS-19103: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCPS-19104: name: "Pipo Saru 2001 [PlayStation 2 The Best]" region: "NTSC-J" @@ -6704,6 +6712,7 @@ SCPS-19151: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCPS-19152: name: "Saru Get You! 2 [PlayStation 2 The Best]" region: "NTSC-J" @@ -7155,6 +7164,7 @@ SCPS-55001: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCPS-55002: name: "Zero" region: "NTSC-J" @@ -7427,6 +7437,7 @@ SCPS-56001: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCPS-56002: name: "Tekken Tag Tournament" region: "NTSC-K" @@ -7602,6 +7613,7 @@ SCUS-97113: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCUS-97114: name: "NBA ShootOut 2001" region: "NTSC-U" @@ -7787,6 +7799,7 @@ SCUS-97159: gsHWFixes: mipmap: 1 halfPixelOffset: 1 # Fixes effect misalignment. + moveHandler: "MV_Ico" # Fixes depth buffer post-processing. SCUS-97160: name: "Extermination [Demo]" region: "NTSC-U" diff --git a/pcsx2/GS/GSCrc.cpp b/pcsx2/GS/GSCrc.cpp index 201ba72d30..da931d9b60 100644 --- a/pcsx2/GS/GSCrc.cpp +++ b/pcsx2/GS/GSCrc.cpp @@ -26,13 +26,6 @@ const CRC::Game CRC::m_games[] = { // Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games. {0x00000000, NoTitle /* NoRegion */}, - {0x6F8545DB, ICO /* US */}, - {0x48CDF317, ICO /* US */}, // Demo - {0xB01A4C95, ICO /* JP */}, - {0x2DF2C1EA, ICO /* KO */}, - {0x5C991F4E, ICO /* EU */}, - {0x788D8B4F, ICO /* EU */}, - {0x29C28734, ICO /* CH */}, {0xFC46EA61, Tekken5 /* JP */}, {0x1F88EE37, Tekken5 /* EU */}, {0x1F88BECD, Tekken5 /* EU */}, // language selector... diff --git a/pcsx2/GS/GSCrc.h b/pcsx2/GS/GSCrc.h index 08cd40490f..d10b5ab4d3 100644 --- a/pcsx2/GS/GSCrc.h +++ b/pcsx2/GS/GSCrc.h @@ -23,7 +23,6 @@ public: enum Title : u32 { NoTitle, - ICO, SMTNocturne, Tekken5, TitleCount, diff --git a/pcsx2/GS/Renderers/HW/GSHwHack.cpp b/pcsx2/GS/Renderers/HW/GSHwHack.cpp index a5930058c5..5f07b9a380 100644 --- a/pcsx2/GS/Renderers/HW/GSHwHack.cpp +++ b/pcsx2/GS/Renderers/HW/GSHwHack.cpp @@ -616,7 +616,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip) // have to set up the palette ourselves too, since GSC executes before it does r.m_mem.m_clut.Read32(RTEX0, r.m_draw_env->TEXA); std::shared_ptr palette = - g_texture_cache->LookupPaletteObject(GSLocalMemory::m_psm[RTEX0.PSM].pal, true); + g_texture_cache->LookupPaletteObject(r.m_mem.m_clut, GSLocalMemory::m_psm[RTEX0.PSM].pal, true); if (!palette) return false; @@ -1167,7 +1167,6 @@ static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GIF return true; } -#if 0 // Disabled to avoid compiler warnings, enable when it is needed. static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GSTextureCache::Target** dst, bool req_target = false, bool preserve_target = false) @@ -1175,7 +1174,8 @@ static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GST return GetMoveTargetPair(r, src, GIFRegTEX0::Create(RSBP, RSBW, RSPSM), dst, GIFRegTEX0::Create(RDBP, RDBW, RDPSM), req_target, preserve_target); } -#endif + +static int s_last_hacked_move_n = 0; bool GSHwHack::MV_Growlanser(GSRendererHW& r) { @@ -1192,8 +1192,7 @@ bool GSHwHack::MV_Growlanser(GSRendererHW& r) return false; // All the moves happen inbetween two draws, so we can take advantage of that to know when to stop. - static int last_hacked_move_n = 0; - if (r.s_n == last_hacked_move_n) + if (r.s_n == s_last_hacked_move_n) return true; GSTextureCache::Target *src, *dst; @@ -1212,7 +1211,53 @@ bool GSHwHack::MV_Growlanser(GSRendererHW& r) g_gs_device->StretchRect(src->GetTexture(), GSVector4(rc) / GSVector4(src->GetUnscaledSize()).xyxy(), dst->GetTexture(), GSVector4(rc) * GSVector4(dst->GetScale()), ShaderConvert::RGBA8_TO_FLOAT32, false); - last_hacked_move_n = r.s_n; + s_last_hacked_move_n = r.s_n; + return true; +} + +bool GSHwHack::MV_Ico(GSRendererHW& r) +{ + // Ico unswizzles the depth buffer (usually) 0x1800 to (usually) 0x2800 with a Z32->C32 move. + // Then it does a bunch of P4 moves to shift the bits in the blue channel to the alpha channel. + // The shifted target then gets used as a P8H texture, basically mapping depth bits 16..24 to a LUT. + // We can't currently HLE that in the usual move handler, so instead, emulate it with a channel shuffle. + + // If we've started skipping moves (i.e. HLE'ed the first one), skip the others. + if (r.s_n == s_last_hacked_move_n && RSPSM == PSMT4 && RDPSM == PSMT4) + return true; + + // 512x448 moves from C32->Z32. + if (RSPSM != PSMZ32 || RDPSM != PSMCT32 || RWIDTH < 512 || RHEIGHT < 448) + return false; + + GL_PUSH("MV_Ico: %x -> %x %dx%d", RSBP, RDBP, RWIDTH, RHEIGHT); + + GSTextureCache::Target *src, *dst; + if (!GetMoveTargetPair(r, &src, &dst, false, false)) + return false; + + // Store B -> A using a channel shuffle. + u32 pal[256]; + for (u32 i = 0; i < std::size(pal); i++) + pal[i] = i << 24; + std::shared_ptr palette = g_texture_cache->LookupPaletteObject(pal, 256, true); + if (!palette) + return false; + + const GSVector4i draw_rc = GSVector4i(0, 0, RWIDTH, RHEIGHT); + dst->UpdateValidChannels(PSMCT32, 0); + dst->UpdateValidity(draw_rc); + + GSHWDrawConfig& config = GSRendererHW::GetInstance()->BeginHLEHardwareDraw(dst->GetTexture(), nullptr, + dst->GetScale(), src->GetTexture(), src->GetScale(), draw_rc); + config.pal = palette->GetPaletteGSTexture(); + config.ps.channel = ChannelFetch_BLUE; + config.ps.depth_fmt = 1; + config.ps.tfx = TFX_DECAL; // T -> A. + config.ps.tcc = true; + GSRendererHW::GetInstance()->EndHLEHardwareDraw(false); + + s_last_hacked_move_n = r.s_n; return true; } @@ -1291,6 +1336,7 @@ const GSHwHack::Entry GSHwHack::s_before_draw_functions[] const GSHwHack::Entry GSHwHack::s_move_handler_functions[] = { CRC_F(MV_Growlanser), + CRC_F(MV_Ico), }; #undef CRC_F diff --git a/pcsx2/GS/Renderers/HW/GSHwHack.h b/pcsx2/GS/Renderers/HW/GSHwHack.h index 0cbba30516..e25f31a990 100644 --- a/pcsx2/GS/Renderers/HW/GSHwHack.h +++ b/pcsx2/GS/Renderers/HW/GSHwHack.h @@ -58,6 +58,7 @@ public: static bool OI_HauntingGround(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); static bool MV_Growlanser(GSRendererHW& r); + static bool MV_Ico(GSRendererHW& r); template struct Entry diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index f940052116..1bffb78da6 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -3972,40 +3972,6 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, m_conf.tex = tex->m_texture; m_conf.pal = tex->m_palette; - if (m_game.title == CRC::ICO) - { - const GSVertex* v = &m_vertex.buff[0]; - const GSVideoMode mode = GetVideoMode(); - if (tex && m_vt.m_primclass == GS_SPRITE_CLASS && m_vertex.next == 2 && PRIM->ABE && // Blend texture - ((v[1].U == 8200 && v[1].V == 7176 && mode == GSVideoMode::NTSC) || // at display resolution 512x448 - (v[1].U == 8200 && v[1].V == 8200 && mode == GSVideoMode::PAL)) && // at display resolution 512x512 - tex->m_TEX0.PSM == PSMT8H) // i.e. read the alpha channel of a 32 bits texture - { - // Note potentially we can limit to TBP0:0x2800 - - // Depth buffer was moved so GS will invalide it which means a - // downscale. ICO uses the MSB depth bits as the texture alpha - // channel. However this depth of field effect requires - // texel:pixel mapping accuracy. - // - // Use an HLE shader to sample depth directly as the alpha channel - GL_INS("ICO sample depth as alpha"); - m_conf.require_full_barrier = true; - // Extract the depth as palette index - m_conf.ps.depth_fmt = 1; - m_conf.ps.channel = ChannelFetch_BLUE; - m_conf.tex = ds->m_texture; - - // We need the palette to convert the depth to the correct alpha value. - if (!tex->m_palette) - { - const u16 pal = GSLocalMemory::m_psm[tex->m_TEX0.PSM].pal; - g_texture_cache->AttachPaletteToSource(tex, pal, true); - m_conf.pal = tex->m_palette; - } - } - } - // Hazard handling (i.e. reading from the current RT/DS). GSTextureCache::SourceRegion source_region = tex->GetRegion(); bool target_region = (tex->IsFromTarget() && source_region.HasEither()); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index f8cf6989da..da3c3af57b 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -4494,9 +4494,9 @@ GSTexture* GSTextureCache::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVec return nullptr; } -std::shared_ptr GSTextureCache::LookupPaletteObject(u16 pal, bool need_gs_texture) +std::shared_ptr GSTextureCache::LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture) { - return m_palette_map.LookupPalette(pal, need_gs_texture); + return m_palette_map.LookupPalette(clut, pal, need_gs_texture); } void GSTextureCache::Read(Target* t, const GSVector4i& r) @@ -5615,13 +5615,13 @@ void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture* // GSTextureCache::Palette -GSTextureCache::Palette::Palette(u16 pal, bool need_gs_texture) +GSTextureCache::Palette::Palette(const u32* clut, u16 pal, bool need_gs_texture) : m_tex_palette(nullptr) , m_pal(pal) { const u16 palette_size = pal * sizeof(u32); m_clut = (u32*)_aligned_malloc(palette_size, 64); - memcpy(m_clut, (const u32*)g_gs_renderer->m_mem.m_clut, palette_size); + memcpy(m_clut, clut, palette_size); if (need_gs_texture) { InitializeTexture(); @@ -5699,6 +5699,11 @@ GSTextureCache::PaletteMap::PaletteMap() } std::shared_ptr GSTextureCache::PaletteMap::LookupPalette(u16 pal, bool need_gs_texture) +{ + return LookupPalette(g_gs_renderer->m_mem.m_clut, pal, need_gs_texture); +} + +std::shared_ptr GSTextureCache::PaletteMap::LookupPalette(const u32* clut, u16 pal, bool need_gs_texture) { ASSERT(pal == 16 || pal == 256); @@ -5707,8 +5712,6 @@ std::shared_ptr GSTextureCache::PaletteMap::LookupPalet // pal == 256 : index 1 auto& map = m_maps[pal == 16 ? 0 : 1]; - const u32* clut = (const u32*)g_gs_renderer->m_mem.m_clut; - // Create PaletteKey for searching into map (clut is actually not copied, so do not store this key into the map) const PaletteKey palette_key = {clut, pal}; @@ -5763,7 +5766,7 @@ std::shared_ptr GSTextureCache::PaletteMap::LookupPalet } } - std::shared_ptr palette = std::make_shared(pal, need_gs_texture); + std::shared_ptr palette = std::make_shared(clut, pal, need_gs_texture); map.emplace(palette->GetPaletteKey(), palette); diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index f9f6a95645..da39db2c56 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -181,7 +181,7 @@ public: std::pair m_alpha_minmax; public: - Palette(u16 pal, bool need_gs_texture); + Palette(const u32* clut, u16 pal, bool need_gs_texture); ~Palette(); __fi std::pair GetAlphaMinMax() const { return m_alpha_minmax; } @@ -335,6 +335,7 @@ public: // Retrieves a shared pointer to a valid Palette from m_maps or creates a new one adding it to the data structure std::shared_ptr LookupPalette(u16 pal, bool need_gs_texture); + std::shared_ptr LookupPalette(const u32* clut, u16 pal, bool need_gs_texture); void Clear(); // Clears m_maps, thus deletes Palette objects }; @@ -470,7 +471,7 @@ public: void DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVector4i src_r); GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size); - std::shared_ptr LookupPaletteObject(u16 pal, bool need_gs_texture); + std::shared_ptr LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture); Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle); Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette = false);