mirror of https://github.com/PCSX2/pcsx2.git
GS: Move HW hacks into their own file and remove from GSState
This commit is contained in:
parent
2db6bf399e
commit
069196704e
|
@ -575,6 +575,7 @@ set(pcsx2GSHeaders
|
|||
GS/Renderers/Null/GSDeviceNull.h
|
||||
GS/Renderers/Null/GSRendererNull.h
|
||||
GS/Renderers/Null/GSTextureNull.h
|
||||
GS/Renderers/HW/GSHwHack.h
|
||||
GS/Renderers/HW/GSRendererHW.h
|
||||
GS/Renderers/HW/GSTextureCache.h
|
||||
GS/Renderers/HW/GSTextureReplacements.h
|
||||
|
|
|
@ -275,7 +275,6 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
|||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
const int gamecrc_options = g_gs_renderer->GetGameCRCOptions();
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
g_gs_device->Destroy();
|
||||
|
@ -332,7 +331,7 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
|||
return false;
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc, gamecrc_options);
|
||||
g_gs_renderer->SetGameCRC(gamecrc, GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -559,9 +558,9 @@ void GSThrottlePresentation()
|
|||
Threading::SleepUntil(s_next_manual_present_time);
|
||||
}
|
||||
|
||||
void GSsetGameCRC(u32 crc, int options)
|
||||
void GSsetGameCRC(u32 crc)
|
||||
{
|
||||
g_gs_renderer->SetGameCRC(crc, options);
|
||||
g_gs_renderer->SetGameCRC(crc, GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack));
|
||||
}
|
||||
|
||||
GSVideoMode GSgetDisplayMode()
|
||||
|
@ -709,8 +708,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
|||
if (GSConfig.CRCHack != old_config.CRCHack ||
|
||||
GSConfig.PointListPalette != old_config.PointListPalette)
|
||||
{
|
||||
// for automatic mipmaps, we need to reload the crc
|
||||
g_gs_renderer->SetGameCRC(g_gs_renderer->GetGameCRC(), g_gs_renderer->GetGameCRCOptions());
|
||||
g_gs_renderer->SetGameCRC(g_gs_renderer->GetGameCRC(), GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack));
|
||||
}
|
||||
|
||||
// renderer-specific options (e.g. auto flush, TC offset)
|
||||
|
|
|
@ -69,7 +69,7 @@ bool GSBeginCapture(std::string filename);
|
|||
void GSEndCapture();
|
||||
void GSPresentCurrentFrame();
|
||||
void GSThrottlePresentation();
|
||||
void GSsetGameCRC(u32 crc, int options);
|
||||
void GSsetGameCRC(u32 crc);
|
||||
|
||||
GSVideoMode GSgetDisplayMode();
|
||||
void GSgetInternalResolution(int* width, int* height);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
class CRC
|
||||
{
|
||||
public:
|
||||
enum Title
|
||||
enum Title : u16
|
||||
{
|
||||
NoTitle,
|
||||
AceCombat4,
|
||||
|
@ -83,7 +83,7 @@ public:
|
|||
TitleCount,
|
||||
};
|
||||
|
||||
enum Region
|
||||
enum Region : u8
|
||||
{
|
||||
NoRegion,
|
||||
US,
|
||||
|
|
|
@ -39,16 +39,12 @@ static __fi bool IsFirstProvokingVertex()
|
|||
|
||||
GSState::GSState()
|
||||
: m_version(STATE_VERSION)
|
||||
, m_gsc(NULL)
|
||||
, m_skip(0)
|
||||
, m_skip_offset(0)
|
||||
, m_q(1.0f)
|
||||
, m_scanmask_used(0)
|
||||
, tex_flushed(true)
|
||||
, m_vt(this, IsFirstProvokingVertex())
|
||||
, m_regs(NULL)
|
||||
, m_crc(0)
|
||||
, m_options(0)
|
||||
{
|
||||
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
|
||||
// Let's keep it disabled to ease debug.
|
||||
|
@ -57,10 +53,6 @@ GSState::GSState()
|
|||
|
||||
s_n = 0;
|
||||
|
||||
m_crc_hack_level = GSConfig.CRCHack;
|
||||
if (m_crc_hack_level == CRCHackLevel::Automatic)
|
||||
m_crc_hack_level = GSUtil::GetRecommendedCRCHackLevel(GSConfig.Renderer);
|
||||
|
||||
memset(&m_v, 0, sizeof(m_v));
|
||||
memset(&m_vertex, 0, sizeof(m_vertex));
|
||||
memset(&m_index, 0, sizeof(m_index));
|
||||
|
@ -2759,12 +2751,10 @@ int GSState::Defrost(const freezeData* fd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void GSState::SetGameCRC(u32 crc, int options)
|
||||
void GSState::SetGameCRC(u32 crc, CRCHackLevel level)
|
||||
{
|
||||
m_crc = crc;
|
||||
m_options = options;
|
||||
m_game = CRC::Lookup(m_crc_hack_level != CRCHackLevel::Off ? crc : 0);
|
||||
SetupCrcHack();
|
||||
m_game = CRC::Lookup((level != CRCHackLevel::Off) ? crc : 0);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -29,19 +29,6 @@
|
|||
#include "GSAlignedClass.h"
|
||||
#include "GSDump.h"
|
||||
|
||||
struct GSFrameInfo
|
||||
{
|
||||
u32 FBP;
|
||||
u32 FPSM;
|
||||
u32 FBMSK;
|
||||
u32 TBP0;
|
||||
u32 TPSM;
|
||||
u32 TZTST;
|
||||
bool TME;
|
||||
};
|
||||
|
||||
typedef bool (*GetSkipCount)(const GSFrameInfo& fi, int& skip);
|
||||
|
||||
class GSState : public GSAlignedClass<32>
|
||||
{
|
||||
public:
|
||||
|
@ -152,15 +139,6 @@ private:
|
|||
void CalcAlphaMinMax();
|
||||
|
||||
protected:
|
||||
bool IsBadFrame();
|
||||
void SetupCrcHack() noexcept;
|
||||
|
||||
bool m_isPackedUV_HackFlag;
|
||||
CRCHackLevel m_crc_hack_level;
|
||||
GetSkipCount m_gsc;
|
||||
int m_skip;
|
||||
int m_skip_offset;
|
||||
|
||||
GSVertex m_v;
|
||||
float m_q;
|
||||
GSVector4i m_scissor;
|
||||
|
@ -168,6 +146,7 @@ protected:
|
|||
|
||||
u8 m_scanmask_used;
|
||||
bool tex_flushed;
|
||||
bool m_isPackedUV_HackFlag;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -239,7 +218,6 @@ public:
|
|||
u32 m_crc;
|
||||
CRC::Game m_game;
|
||||
std::unique_ptr<GSDumpBase> m_dump;
|
||||
int m_options;
|
||||
bool m_nativeres;
|
||||
bool m_mipmap;
|
||||
u32 m_dirty_gs_regs;
|
||||
|
@ -387,8 +365,7 @@ public:
|
|||
int Defrost(const freezeData* fd);
|
||||
|
||||
u32 GetGameCRC() const { return m_crc; }
|
||||
int GetGameCRCOptions() const { return m_options; }
|
||||
virtual void SetGameCRC(u32 crc, int options);
|
||||
virtual void SetGameCRC(u32 crc, CRCHackLevel level);
|
||||
|
||||
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
|
||||
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
|
||||
|
|
|
@ -145,8 +145,11 @@ bool GSUtil::HasCompatibleBits(u32 spsm, u32 dpsm)
|
|||
return (s_maps.CompatibleBitsField[spsm][dpsm >> 5] & (1 << (dpsm & 0x1f))) != 0;
|
||||
}
|
||||
|
||||
CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
|
||||
CRCHackLevel GSUtil::GetEffectiveCRCHackLevel(GSRendererType type, CRCHackLevel level)
|
||||
{
|
||||
if (level != CRCHackLevel::Automatic)
|
||||
return level;
|
||||
|
||||
return (type == GSRendererType::DX11 || type == GSRendererType::DX12) ? CRCHackLevel::Full : CRCHackLevel::Partial;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
static bool HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm);
|
||||
static bool HasCompatibleBits(u32 spsm, u32 dpsm);
|
||||
|
||||
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
|
||||
static CRCHackLevel GetEffectiveCRCHackLevel(GSRendererType type, CRCHackLevel level);
|
||||
static GSRendererType GetPreferredRenderer();
|
||||
};
|
||||
|
||||
|
|
|
@ -14,22 +14,25 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "GS/GSState.h"
|
||||
#include "GS/Renderers/HW/GSRendererHW.h"
|
||||
#include "GS/Renderers/HW/GSHwHack.h"
|
||||
#include "GS/GSGL.h"
|
||||
|
||||
bool s_nativeres;
|
||||
static bool s_nativeres;
|
||||
static CRCHackLevel s_crc_hack_level = CRCHackLevel::Full;
|
||||
// hacks
|
||||
|
||||
#define CRC_Partial (s_crc_hack_level >= CRCHackLevel::Partial)
|
||||
#define CRC_Full (s_crc_hack_level >= CRCHackLevel::Full)
|
||||
#define CRC_Aggressive (s_crc_hack_level >= CRCHackLevel::Aggressive)
|
||||
|
||||
CRC::Region g_crc_region = CRC::NoRegion;
|
||||
#define RPRIM r.PRIM
|
||||
#define RCONTEXT r.m_context
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Partial level, broken on all renderers.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GSC_BigMuthaTruckers(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_BigMuthaTruckers(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -47,7 +50,7 @@ bool GSC_BigMuthaTruckers(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_DeathByDegreesTekkenNinaWilliams(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_DeathByDegreesTekkenNinaWilliams(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
// Note: Game also has issues with texture shuffle not supported on strange clamp mode.
|
||||
// See https://forums.pcsx2.net/Thread-GSDX-Texture-Cache-Bug-Report-Death-By-Degrees-SLUS-20934-NTSC
|
||||
|
@ -77,7 +80,7 @@ bool GSC_DeathByDegreesTekkenNinaWilliams(const GSFrameInfo& fi, int& skip) noex
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_GiTS(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_GiTS(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -92,7 +95,7 @@ bool GSC_GiTS(const GSFrameInfo& fi, int& skip) noexcept
|
|||
}
|
||||
|
||||
// Channel effect not properly supported yet
|
||||
bool GSC_Manhunt2(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Manhunt2(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
/*
|
||||
* The game readback RT as 8 bits index texture to apply a non-linear brightness/gamma correction on all channel
|
||||
|
@ -123,7 +126,7 @@ bool GSC_Manhunt2(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_CrashBandicootWoC(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_CrashBandicootWoC(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
// Channel effect not properly supported - Removes fog to fix the fog wall issue on Direct3D at any resolution, and while upscaling on every Hardware renderer.
|
||||
if (skip == 0)
|
||||
|
@ -153,7 +156,7 @@ bool GSC_CrashBandicootWoC(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SacredBlaze(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_SacredBlaze(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
//Fix Sacred Blaze rendering glitches
|
||||
if (skip == 0)
|
||||
|
@ -167,7 +170,7 @@ bool GSC_SacredBlaze(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Spartan(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Spartan(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -186,7 +189,7 @@ bool GSC_Spartan(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Oneechanbara2Special(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Oneechanbara2Special(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -201,7 +204,7 @@ bool GSC_Oneechanbara2Special(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SakuraTaisen(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_SakuraTaisen(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -233,7 +236,7 @@ bool GSC_SakuraTaisen(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SFEX3(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_SFEX3(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -248,7 +251,7 @@ bool GSC_SFEX3(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Tekken5(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Tekken5(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -260,7 +263,7 @@ bool GSC_Tekken5(const GSFrameInfo& fi, int& skip) noexcept
|
|||
// Let's enable this hack for Aggressive only since it's an upscaling issue for both renders.
|
||||
skip = 95;
|
||||
}
|
||||
else if (fi.TZTST == 1 && fi.TME && (fi.FBP == 0x02bc0 || fi.FBP == 0x02be0 || fi.FBP == 0x02d00 || fi.FBP == 0x03480 || fi.FBP == 0x034a0) && fi.FPSM == fi.TPSM && fi.TBP0 == 0x00000 && fi.TPSM == PSM_PSMCT32)
|
||||
else if (fi.ZTST == 1 && fi.TME && (fi.FBP == 0x02bc0 || fi.FBP == 0x02be0 || fi.FBP == 0x02d00 || fi.FBP == 0x03480 || fi.FBP == 0x034a0) && fi.FPSM == fi.TPSM && fi.TBP0 == 0x00000 && fi.TPSM == PSM_PSMCT32)
|
||||
{
|
||||
// The moving display effect(flames) is not emulated properly in the entire screen so let's remove the effect in the stage: Burning Temple. Related to half screen bottom issue.
|
||||
// Fixes black lines in the stage: Burning Temple - caused by upscaling. Note the black lines can also be fixed with Merge Sprite hack.
|
||||
|
@ -271,7 +274,7 @@ bool GSC_Tekken5(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TombRaiderAnniversary(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_TombRaiderAnniversary(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -284,7 +287,7 @@ bool GSC_TombRaiderAnniversary(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TombRaiderLegend(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_TombRaiderLegend(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -302,7 +305,7 @@ bool GSC_TombRaiderLegend(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TombRaiderUnderWorld(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_TombRaiderUnderWorld(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -319,7 +322,7 @@ bool GSC_TombRaiderUnderWorld(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_BurnoutGames(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_BurnoutGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -335,7 +338,7 @@ bool GSC_BurnoutGames(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_MidnightClub3(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_MidnightClub3(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -351,7 +354,7 @@ bool GSC_MidnightClub3(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TalesOfLegendia(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_TalesOfLegendia(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -380,7 +383,7 @@ bool GSC_TalesOfLegendia(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Kunoichi(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Kunoichi(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -405,7 +408,7 @@ bool GSC_Kunoichi(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_ZettaiZetsumeiToshi2(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -454,7 +457,7 @@ bool GSC_ZettaiZetsumeiToshi2(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SakuraWarsSoLongMyLove(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -475,7 +478,7 @@ bool GSC_SakuraWarsSoLongMyLove(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_FightingBeautyWulong(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_FightingBeautyWulong(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -490,7 +493,7 @@ bool GSC_FightingBeautyWulong(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_GodHand(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_GodHand(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -503,7 +506,7 @@ bool GSC_GodHand(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_KnightsOfTheTemple2(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_KnightsOfTheTemple2(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -520,7 +523,7 @@ bool GSC_KnightsOfTheTemple2(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_UltramanFightingEvolution(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_UltramanFightingEvolution(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -534,7 +537,7 @@ bool GSC_UltramanFightingEvolution(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TalesofSymphonia(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_TalesofSymphonia(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -551,7 +554,7 @@ bool GSC_TalesofSymphonia(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Simple2000Vol114(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Simple2000Vol114(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -571,7 +574,7 @@ bool GSC_Simple2000Vol114(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_UrbanReign(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_UrbanReign(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -584,7 +587,7 @@ bool GSC_UrbanReign(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SteambotChronicles(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_SteambotChronicles(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -609,7 +612,7 @@ bool GSC_SteambotChronicles(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_YakuzaGames(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_YakuzaGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -631,7 +634,7 @@ bool GSC_YakuzaGames(const GSFrameInfo& fi, int& skip) noexcept
|
|||
// Full level, correctly emulated on OpenGL/Vulkan but can be used as potential speed hack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GSC_GetawayGames(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_GetawayGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -648,7 +651,7 @@ bool GSC_GetawayGames(const GSFrameInfo& fi, int& skip) noexcept
|
|||
// Aggressive only hack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GSC_AceCombat4(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_AceCombat4(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
// Removes clouds for a good speed boost, removes both 3D clouds(invisible with Hardware renderers, but cause slowdown) and 2D background clouds.
|
||||
// Removes blur from player airplane.
|
||||
|
@ -666,7 +669,7 @@ bool GSC_AceCombat4(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_FFXGames(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_FFXGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -685,7 +688,7 @@ bool GSC_FFXGames(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_Okami(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_Okami(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -705,7 +708,7 @@ bool GSC_Okami(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_RedDeadRevolver(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_RedDeadRevolver(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -718,7 +721,7 @@ bool GSC_RedDeadRevolver(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_ShinOnimusha(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_ShinOnimusha(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -747,7 +750,7 @@ bool GSC_ShinOnimusha(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSC_XenosagaE3(const GSFrameInfo& fi, int& skip) noexcept
|
||||
bool GSHwHack::GSC_XenosagaE3(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
|
@ -777,113 +780,509 @@ bool GSC_XenosagaE3(const GSFrameInfo& fi, int& skip) noexcept
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GSState::SetupCrcHack() noexcept
|
||||
bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
GetSkipCount lut[CRC::TitleCount];
|
||||
// Rendering pattern:
|
||||
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
|
||||
// game needs to do a depth effect (so green channel to alpha),
|
||||
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
|
||||
// vertical resolution is half so only half is processed at once
|
||||
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
|
||||
|
||||
s_nativeres = m_nativeres;
|
||||
s_crc_hack_level = m_crc_hack_level;
|
||||
const GIFRegTEX0& Texture = RCONTEXT->TEX0;
|
||||
|
||||
memset(lut, 0, sizeof(lut));
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = RCONTEXT->FRAME.FBW;
|
||||
Frame.TBP0 = RCONTEXT->FRAME.Block();
|
||||
|
||||
if (CRC_Partial)
|
||||
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Frame.TBP0 == 0x00a00 && Texture.PSM == PSM_PSMT8H && (r.m_r.y == 256 || r.m_r.y == 224))
|
||||
{
|
||||
lut[CRC::GodHand] = GSC_GodHand;
|
||||
lut[CRC::KnightsOfTheTemple2] = GSC_KnightsOfTheTemple2;
|
||||
lut[CRC::Kunoichi] = GSC_Kunoichi;
|
||||
lut[CRC::Manhunt2] = GSC_Manhunt2;
|
||||
lut[CRC::MidnightClub3] = GSC_MidnightClub3;
|
||||
lut[CRC::SacredBlaze] = GSC_SacredBlaze;
|
||||
lut[CRC::SakuraTaisen] = GSC_SakuraTaisen;
|
||||
lut[CRC::SakuraWarsSoLongMyLove] = GSC_SakuraWarsSoLongMyLove;
|
||||
lut[CRC::Simple2000Vol114] = GSC_Simple2000Vol114;
|
||||
lut[CRC::SFEX3] = GSC_SFEX3;
|
||||
lut[CRC::TalesOfLegendia] = GSC_TalesOfLegendia;
|
||||
lut[CRC::TalesofSymphonia] = GSC_TalesofSymphonia;
|
||||
lut[CRC::TombRaiderAnniversary] = GSC_TombRaiderAnniversary;
|
||||
lut[CRC::TombRaiderLegend] = GSC_TombRaiderLegend;
|
||||
lut[CRC::TombRaiderUnderworld] = GSC_TombRaiderUnderWorld;
|
||||
lut[CRC::UrbanReign] = GSC_UrbanReign;
|
||||
lut[CRC::ZettaiZetsumeiToshi2] = GSC_ZettaiZetsumeiToshi2;
|
||||
// 224 ntsc, 256 pal.
|
||||
GL_INS("OI_BigMuthaTruckers half bottom offset");
|
||||
|
||||
// Channel Effect
|
||||
lut[CRC::CrashBandicootWoC] = GSC_CrashBandicootWoC;
|
||||
lut[CRC::GiTS] = GSC_GiTS;
|
||||
lut[CRC::Spartan] = GSC_Spartan;
|
||||
lut[CRC::SteambotChronicles] = GSC_SteambotChronicles;
|
||||
const size_t count = r.m_vertex.next;
|
||||
GSVertex* v = &r.m_vertex.buff[0];
|
||||
const u16 offset = (u16)r.m_r.y * 16;
|
||||
|
||||
// Depth Issue
|
||||
lut[CRC::BurnoutGames] = GSC_BurnoutGames;
|
||||
|
||||
// Half Screen bottom issue
|
||||
lut[CRC::Tekken5] = GSC_Tekken5;
|
||||
|
||||
// Texture shuffle
|
||||
lut[CRC::BigMuthaTruckers] = GSC_BigMuthaTruckers;
|
||||
lut[CRC::DeathByDegreesTekkenNinaWilliams] = GSC_DeathByDegreesTekkenNinaWilliams; // + Upscaling issues
|
||||
|
||||
// Upscaling hacks
|
||||
lut[CRC::FightingBeautyWulong] = GSC_FightingBeautyWulong;
|
||||
lut[CRC::Oneechanbara2Special] = GSC_Oneechanbara2Special;
|
||||
lut[CRC::UltramanFightingEvolution] = GSC_UltramanFightingEvolution;
|
||||
lut[CRC::YakuzaGames] = GSC_YakuzaGames;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
v[i].V += offset;
|
||||
}
|
||||
|
||||
// CRC FULL is for Direct3D, they are fixed on OpenGL/Vulkan
|
||||
if (CRC_Full)
|
||||
{
|
||||
// Accurate Blending
|
||||
lut[CRC::GetawayGames] = GSC_GetawayGames; // Blending High
|
||||
}
|
||||
|
||||
if (CRC_Aggressive)
|
||||
{
|
||||
lut[CRC::AceCombat4] = GSC_AceCombat4;
|
||||
lut[CRC::FFX2] = GSC_FFXGames;
|
||||
lut[CRC::FFX] = GSC_FFXGames;
|
||||
lut[CRC::FFXII] = GSC_FFXGames;
|
||||
lut[CRC::RedDeadRevolver] = GSC_RedDeadRevolver;
|
||||
lut[CRC::ShinOnimusha] = GSC_ShinOnimusha;
|
||||
lut[CRC::XenosagaE3] = GSC_XenosagaE3;
|
||||
|
||||
// Upscaling issues
|
||||
lut[CRC::Okami] = GSC_Okami;
|
||||
}
|
||||
|
||||
m_gsc = lut[m_game.title];
|
||||
g_crc_region = m_game.region;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!((r.m_r == GSVector4i(0, 0, 16, 16)).alltrue() || (r.m_r == GSVector4i(0, 0, 64, 64)).alltrue()))
|
||||
return true; // Only 16x16 or 64x64 draws.
|
||||
|
||||
// Sprite rendering
|
||||
if (!r.CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
r.SwSpriteRender();
|
||||
|
||||
return false; // Skip current draw
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_FFXII(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
static u32* video = nullptr;
|
||||
static size_t lines = 0;
|
||||
|
||||
if (lines == 0)
|
||||
{
|
||||
if (r.m_vt.m_primclass == GS_LINE_CLASS && (r.m_vertex.next == 448 * 2 || r.m_vertex.next == 512 * 2))
|
||||
{
|
||||
lines = r.m_vertex.next / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r.m_vt.m_primclass == GS_POINT_CLASS)
|
||||
{
|
||||
if (r.m_vertex.next >= 16 * 512)
|
||||
{
|
||||
// incoming pixels are stored in columns, one column is 16x512, total res 448x512 or 448x454
|
||||
|
||||
if (!video)
|
||||
video = new u32[512 * 512];
|
||||
|
||||
const GSVertex* RESTRICT v = r.m_vertex.buff;
|
||||
const int ox = RCONTEXT->XYOFFSET.OFX - 8;
|
||||
const int oy = RCONTEXT->XYOFFSET.OFY - 8;
|
||||
|
||||
for (int i = (int)r.m_vertex.next; i > 0; i--, v++)
|
||||
{
|
||||
const int x = (v->XYZ.X - ox) >> 4;
|
||||
const int y = (v->XYZ.Y - oy) >> 4;
|
||||
|
||||
if (x < 0 || x >= 448 || y < 0 || y >= (int)lines)
|
||||
return false; // le sigh
|
||||
|
||||
video[(y << 8) + (y << 7) + (y << 6) + x] = v->RGBAQ.U32[0];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = 0;
|
||||
}
|
||||
}
|
||||
else if (r.m_vt.m_primclass == GS_LINE_CLASS)
|
||||
{
|
||||
if (r.m_vertex.next == lines * 2)
|
||||
{
|
||||
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
|
||||
// but we use the stored video data to create a new texture, and replace the lines with two triangles
|
||||
|
||||
g_gs_device->Recycle(t->m_texture);
|
||||
|
||||
t->m_texture = g_gs_device->CreateTexture(512, 512, 1, GSTexture::Format::Color);
|
||||
|
||||
t->m_texture->Update(GSVector4i(0, 0, 448, lines), video, 448 * 4);
|
||||
|
||||
r.m_vertex.buff[2] = r.m_vertex.buff[r.m_vertex.next - 2];
|
||||
r.m_vertex.buff[3] = r.m_vertex.buff[r.m_vertex.next - 1];
|
||||
|
||||
r.m_index.buff[0] = 0;
|
||||
r.m_index.buff[1] = 1;
|
||||
r.m_index.buff[2] = 2;
|
||||
r.m_index.buff[3] = 1;
|
||||
r.m_index.buff[4] = 2;
|
||||
r.m_index.buff[5] = 3;
|
||||
|
||||
r.m_vertex.head = r.m_vertex.tail = r.m_vertex.next = 4;
|
||||
r.m_index.tail = 6;
|
||||
|
||||
r.m_vt.Update(r.m_vertex.buff, r.m_index.buff, r.m_vertex.tail, r.m_index.tail, GS_TRIANGLE_CLASS);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
const u32 FBP = RCONTEXT->FRAME.Block();
|
||||
const u32 ZBP = RCONTEXT->ZBUF.Block();
|
||||
const u32 TBP = RCONTEXT->TEX0.TBP0;
|
||||
|
||||
if ((FBP == 0x00d00 || FBP == 0x00000) && ZBP == 0x02100 && RPRIM->TME && TBP == 0x01a00 && RCONTEXT->TEX0.PSM == PSM_PSMCT16S)
|
||||
{
|
||||
// random battle transition (z buffer written directly, clear it now)
|
||||
GL_INS("OI_FFX ZB clear");
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_MetalSlug6(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// missing red channel fix (looks alright in pcsx2 r5000+)
|
||||
|
||||
GSVertex* RESTRICT v = r.m_vertex.buff;
|
||||
|
||||
for (size_t i = r.m_vertex.next; i > 0; i--, v++)
|
||||
{
|
||||
const u32 c = v->RGBAQ.U32[0];
|
||||
|
||||
const u32 r = (c >> 0) & 0xff;
|
||||
const u32 g = (c >> 8) & 0xff;
|
||||
const u32 b = (c >> 16) & 0xff;
|
||||
|
||||
if (r == 0 && g != 0 && b != 0)
|
||||
{
|
||||
v->RGBAQ.U32[0] = (c & 0xffffff00) | ((g + b + 1) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
r.m_vt.Update(r.m_vertex.buff, r.m_index.buff, r.m_vertex.tail, r.m_index.tail, r.m_vt.m_primclass);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!RPRIM->TME)
|
||||
{
|
||||
const u32 FBP = RCONTEXT->FRAME.Block();
|
||||
const u32 ZBP = RCONTEXT->ZBUF.Block();
|
||||
|
||||
if (FBP == 0x008c0 && ZBP == 0x01a40)
|
||||
{
|
||||
// frame buffer clear, atst = fail, afail = write z only, z buffer points to frame buffer
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
|
||||
TEX0.TBP0 = ZBP;
|
||||
TEX0.TBW = RCONTEXT->FRAME.FBW;
|
||||
TEX0.PSM = RCONTEXT->FRAME.PSM;
|
||||
|
||||
if (GSTextureCache::Target* tmp_rt = r.m_tc->LookupTarget(TEX0, r.GetTargetSize(), GSTextureCache::RenderTarget, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
||||
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (FBP == 0x00000 && RCONTEXT->ZBUF.Block() == 0x01180)
|
||||
{
|
||||
// z buffer clear, frame buffer now points to the z buffer (how can they be so clever?)
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
|
||||
TEX0.TBP0 = FBP;
|
||||
TEX0.TBW = RCONTEXT->FRAME.FBW;
|
||||
TEX0.PSM = RCONTEXT->ZBUF.PSM;
|
||||
|
||||
if (GSTextureCache::Target* tmp_ds = r.m_tc->LookupTarget(TEX0, r.GetTargetSize(), GSTextureCache::DepthStencil, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
||||
g_gs_device->ClearDepth(tmp_ds->m_texture);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// Rendering pattern is:
|
||||
// Save RG channel with a kind of a TS (replaced by a copy in this hack),
|
||||
// compute shadow in RG,
|
||||
// save result in alpha with a TS,
|
||||
// Restore RG channel that we previously copied to render shadows.
|
||||
|
||||
const GIFRegTEX0& Texture = RCONTEXT->TEX0;
|
||||
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = RCONTEXT->FRAME.FBW;
|
||||
Frame.TBP0 = RCONTEXT->FRAME.Block();
|
||||
Frame.PSM = RCONTEXT->FRAME.PSM;
|
||||
|
||||
if ((!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16))
|
||||
return true;
|
||||
|
||||
GL_INS("OI_SonicUnleashed replace draw by a copy");
|
||||
|
||||
GSTextureCache::Target* src = r.m_tc->LookupTarget(Texture, GSVector2i(1, 1), GSTextureCache::RenderTarget, true);
|
||||
|
||||
const GSVector2i rt_size(rt->GetSize());
|
||||
const GSVector2i src_size(src->m_texture->GetSize());
|
||||
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
||||
|
||||
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
|
||||
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
|
||||
|
||||
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GSHwHack::OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// world map clipping
|
||||
//
|
||||
// The bad draw call is a sprite rendering to clear the z buffer
|
||||
|
||||
/*
|
||||
Depth buffer description
|
||||
* width is 10 pages
|
||||
* texture/scissor size is 640x448
|
||||
* depth is 16 bits so it writes 70 (10w * 7h) pages of data.
|
||||
|
||||
following draw calls will use the buffer as 6 pages width with a scissor
|
||||
test of 384x672. So the above texture can be seen as a
|
||||
|
||||
* texture width: 6 pages * 64 pixels/page = 384
|
||||
* texture height: 70/6 pages * 64 pixels/page =746
|
||||
|
||||
So as you can see the GS issue a write of 640x448 but actually it
|
||||
expects to clean a 384x746 area. Ideally the fix will transform the
|
||||
buffer to adapt the page width properly.
|
||||
*/
|
||||
|
||||
const GSVertex* v = &r.m_vertex.buff[0];
|
||||
|
||||
if (r.m_vertex.next == 2 && !RPRIM->TME && RCONTEXT->FRAME.FBW == 10 && v->XYZ.Z == 0 && RCONTEXT->TEST.ZTST == ZTST_ALWAYS)
|
||||
{
|
||||
GL_INS("OI_ArTonelico2");
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_JakGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!(r.m_r == GSVector4i(0, 0, 16, 16)).alltrue())
|
||||
return true; // Only 16x16 draws.
|
||||
|
||||
if (!r.CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render 16x16 palette via CPU.
|
||||
r.SwSpriteRender();
|
||||
|
||||
return false; // Skip current draw.
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!r.OI_PointListPalette(r, rt, ds, t))
|
||||
return false; // Render point list palette.
|
||||
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!r.CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render palette via CPU.
|
||||
r.SwSpriteRender();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OO (others output?) hacks: invalidate extra local memory after the draw call
|
||||
|
||||
void GSHwHack::OO_BurnoutGames(GSRendererHW& r)
|
||||
{
|
||||
const GIFRegTEX0& TEX0 = RCONTEXT->TEX0;
|
||||
const GIFRegALPHA& ALPHA = RCONTEXT->ALPHA;
|
||||
const GIFRegFRAME& FRAME = RCONTEXT->FRAME;
|
||||
if (RPRIM->PRIM == GS_SPRITE && !RPRIM->IIP && RPRIM->TME && !RPRIM->FGE && RPRIM->ABE && !RPRIM->AA1 && !RPRIM->FST && !RPRIM->FIX && TEX0.TBW == 16 && TEX0.TW == 10 && TEX0.TCC && !TEX0.TFX && TEX0.PSM == PSM_PSMT8 && TEX0.CPSM == PSM_PSMCT32 && !TEX0.CSM && TEX0.TH == 8 && ALPHA.A == ALPHA.B && ALPHA.D == 0 && FRAME.FBW == 16 && FRAME.PSM == PSM_PSMCT32)
|
||||
{
|
||||
// Readback clouds being rendered during level loading.
|
||||
// Later the alpha channel from the 32 bit frame buffer is used as an 8 bit indexed texture to draw
|
||||
// the clouds on top of the sky at each frame.
|
||||
// Burnout 3 PAL 50Hz: 0x3ba0 => 0x1e80.
|
||||
GL_INS("OO_BurnoutGames - Readback clouds renderered from TEX0.TBP0 = 0x%04x (TEX0.CBP = 0x%04x) to FBP = 0x%04x", TEX0.TBP0, TEX0.CBP, FRAME.Block());
|
||||
r.m_tc->InvalidateLocalMem(RCONTEXT->offset.fb, r.m_r);
|
||||
}
|
||||
}
|
||||
|
||||
#undef RCONTEXT
|
||||
#undef RPRIM
|
||||
|
||||
#undef CRC_Partial
|
||||
#undef CRC_Full
|
||||
#undef CRC_Aggressive
|
||||
|
||||
bool GSState::IsBadFrame()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_gsc_functions[] = {
|
||||
{CRC::GodHand, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_GodHand},
|
||||
{CRC::KnightsOfTheTemple2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_KnightsOfTheTemple2},
|
||||
{CRC::Kunoichi, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Kunoichi},
|
||||
{CRC::Manhunt2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Manhunt2},
|
||||
{CRC::MidnightClub3, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_MidnightClub3},
|
||||
{CRC::SacredBlaze, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SacredBlaze},
|
||||
{CRC::SakuraTaisen, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SakuraTaisen},
|
||||
{CRC::SakuraWarsSoLongMyLove, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SakuraWarsSoLongMyLove},
|
||||
{CRC::Simple2000Vol114, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Simple2000Vol114},
|
||||
{CRC::SFEX3, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SFEX3},
|
||||
{CRC::TalesOfLegendia, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TalesOfLegendia},
|
||||
{CRC::TalesofSymphonia, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TalesofSymphonia},
|
||||
{CRC::TombRaiderAnniversary, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderAnniversary},
|
||||
{CRC::TombRaiderLegend, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderLegend},
|
||||
{CRC::TombRaiderUnderworld, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderUnderWorld},
|
||||
{CRC::UrbanReign, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_UrbanReign},
|
||||
{CRC::ZettaiZetsumeiToshi2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_ZettaiZetsumeiToshi2},
|
||||
|
||||
// Channel Effect
|
||||
{CRC::CrashBandicootWoC, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_CrashBandicootWoC},
|
||||
{CRC::GiTS, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_GiTS},
|
||||
{CRC::Spartan, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Spartan},
|
||||
{CRC::SteambotChronicles, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SteambotChronicles},
|
||||
|
||||
// Depth Issue
|
||||
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_BurnoutGames},
|
||||
|
||||
// Half Screen bottom issue
|
||||
{CRC::Tekken5, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Tekken5},
|
||||
|
||||
// Texture shuffle
|
||||
{CRC::BigMuthaTruckers, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_BigMuthaTruckers},
|
||||
{CRC::DeathByDegreesTekkenNinaWilliams, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_DeathByDegreesTekkenNinaWilliams}, // + Upscaling issues
|
||||
|
||||
// Upscaling hacks
|
||||
{CRC::FightingBeautyWulong, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_FightingBeautyWulong},
|
||||
{CRC::Oneechanbara2Special, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Oneechanbara2Special},
|
||||
{CRC::UltramanFightingEvolution, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_UltramanFightingEvolution},
|
||||
{CRC::YakuzaGames, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_YakuzaGames},
|
||||
|
||||
// Accurate Blending
|
||||
{CRC::GetawayGames, CRC::RegionCount, CRCHackLevel::Full, &GSHwHack::GSC_GetawayGames}, // Blending High
|
||||
|
||||
{CRC::AceCombat4, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_AceCombat4},
|
||||
{CRC::FFX2, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames},
|
||||
{CRC::FFX, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames},
|
||||
{CRC::FFXII, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames},
|
||||
{CRC::RedDeadRevolver, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_RedDeadRevolver},
|
||||
{CRC::ShinOnimusha, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_ShinOnimusha},
|
||||
{CRC::XenosagaE3, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_XenosagaE3},
|
||||
|
||||
// Upscaling issues
|
||||
{CRC::Okami, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_Okami},
|
||||
};
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_oi_functions[] = {
|
||||
{CRC::BigMuthaTruckers, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BigMuthaTruckers},
|
||||
{CRC::DBZBT2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_DBZBTGames},
|
||||
{CRC::DBZBT3, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_DBZBTGames},
|
||||
{CRC::FFXII, CRC::EU, CRCHackLevel::Minimum, &GSHwHack::OI_FFXII},
|
||||
{CRC::FFX, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_FFX},
|
||||
{CRC::MetalSlug6, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_MetalSlug6},
|
||||
{CRC::RozenMaidenGebetGarden, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_RozenMaidenGebetGarden},
|
||||
{CRC::SonicUnleashed, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_SonicUnleashed},
|
||||
{CRC::ArTonelico2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_ArTonelico2},
|
||||
{CRC::Jak2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames},
|
||||
{CRC::Jak3, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames},
|
||||
{CRC::JakX, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames},
|
||||
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BurnoutGames},
|
||||
{CRC::Black, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BurnoutGames}};
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::OO_Ptr> GSHwHack::s_oo_functions[] = {
|
||||
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OO_BurnoutGames},
|
||||
};
|
||||
|
||||
void GSRendererHW::SetupCrcHack(CRCHackLevel level)
|
||||
{
|
||||
GSFrameInfo fi = {0};
|
||||
s_nativeres = m_nativeres;
|
||||
s_crc_hack_level = level;
|
||||
|
||||
fi.FBP = m_context->FRAME.Block();
|
||||
fi.FPSM = m_context->FRAME.PSM;
|
||||
fi.FBMSK = m_context->FRAME.FBMSK;
|
||||
fi.TME = PRIM->TME;
|
||||
fi.TBP0 = m_context->TEX0.TBP0;
|
||||
fi.TPSM = m_context->TEX0.PSM;
|
||||
fi.TZTST = m_context->TEST.ZTST;
|
||||
|
||||
if (m_gsc && !m_gsc(fi, m_skip))
|
||||
m_gsc = nullptr;
|
||||
if (level != CRCHackLevel::Off)
|
||||
{
|
||||
return false;
|
||||
for (const auto& entry : GSHwHack::s_gsc_functions)
|
||||
{
|
||||
if (entry.Test(m_game.title, m_game.region, level))
|
||||
{
|
||||
m_gsc = entry.ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_oi = nullptr;
|
||||
if (level != CRCHackLevel::Off)
|
||||
{
|
||||
for (const auto& entry : GSHwHack::s_oi_functions)
|
||||
{
|
||||
if (entry.Test(m_game.title, m_game.region, level))
|
||||
{
|
||||
m_oi = entry.ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GSConfig.PointListPalette)
|
||||
{
|
||||
if (m_oi)
|
||||
Console.Warning("Overriding m_oi with PointListPalette");
|
||||
|
||||
m_oi = &GSRendererHW::OI_PointListPalette;
|
||||
}
|
||||
|
||||
m_oo = nullptr;
|
||||
if (level != CRCHackLevel::Off)
|
||||
{
|
||||
for (const auto& entry : GSHwHack::s_oo_functions)
|
||||
{
|
||||
if (entry.Test(m_game.title, m_game.region, level))
|
||||
{
|
||||
m_oo = entry.ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsBadFrame()
|
||||
{
|
||||
if (m_gsc)
|
||||
{
|
||||
const GSFrameInfo fi = {
|
||||
m_context->FRAME.Block(),
|
||||
m_context->FRAME.PSM,
|
||||
m_context->FRAME.FBMSK,
|
||||
m_context->ZBUF.Block(),
|
||||
m_context->ZBUF.ZMSK,
|
||||
m_context->TEST.ZTST,
|
||||
PRIM->TME,
|
||||
m_context->TEX0.TBP0,
|
||||
m_context->TEX0.PSM,
|
||||
};
|
||||
|
||||
if (!m_gsc(*this, fi, m_skip))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_skip == 0 && GSConfig.SkipDrawEnd > 0)
|
||||
{
|
||||
if (fi.TME)
|
||||
if (PRIM->TME)
|
||||
{
|
||||
// depth textures (bully, mgs3s1 intro, Front Mission 5)
|
||||
// General, often problematic post processing
|
||||
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
|
||||
if (GSLocalMemory::m_psm[m_context->TEX0.PSM].depth ||
|
||||
GSUtil::HasSharedBits(m_context->FRAME.Block(), m_context->FRAME.PSM, m_context->TEX0.TBP0, m_context->TEX0.PSM))
|
||||
{
|
||||
m_skip_offset = GSConfig.SkipDrawStart;
|
||||
m_skip = GSConfig.SkipDrawEnd;
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "GS/Renderers/HW/GSRendererHW.h"
|
||||
|
||||
class GSHwHack
|
||||
{
|
||||
public:
|
||||
static bool GSC_BigMuthaTruckers(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_DeathByDegreesTekkenNinaWilliams(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_GiTS(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Manhunt2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_CrashBandicootWoC(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_SacredBlaze(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Spartan(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Oneechanbara2Special(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_SakuraTaisen(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_SFEX3(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Tekken5(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_TombRaiderAnniversary(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_TombRaiderLegend(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_TombRaiderUnderWorld(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_BurnoutGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_MidnightClub3(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_TalesOfLegendia(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Kunoichi(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_FightingBeautyWulong(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_GodHand(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_KnightsOfTheTemple2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_TalesofSymphonia(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Simple2000Vol114(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_UrbanReign(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_SteambotChronicles(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_YakuzaGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_GetawayGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_AceCombat4(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_FFXGames(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_Okami(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_RedDeadRevolver(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_ShinOnimusha(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
static bool GSC_XenosagaE3(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
|
||||
|
||||
static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_FFXII(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_MetalSlug6(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_JakGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
static void OO_BurnoutGames(GSRendererHW& r);
|
||||
|
||||
template <typename F>
|
||||
struct Entry
|
||||
{
|
||||
CRC::Title game;
|
||||
CRC::Region region;
|
||||
CRCHackLevel level;
|
||||
F ptr;
|
||||
|
||||
__fi bool Test(CRC::Title title_, CRC::Region region_, CRCHackLevel level_) const
|
||||
{
|
||||
return (game == title_ && (region == CRC::RegionCount || region == region_) && level_ >= level);
|
||||
}
|
||||
};
|
||||
|
||||
static const Entry<GSRendererHW::GSC_Ptr> s_gsc_functions[];
|
||||
static const Entry<GSRendererHW::OI_Ptr> s_oi_functions[];
|
||||
static const Entry<GSRendererHW::OO_Ptr> s_oo_functions[];
|
||||
};
|
|
@ -159,11 +159,11 @@ bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Target* dst, const G
|
|||
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
|
||||
}
|
||||
|
||||
void GSRendererHW::SetGameCRC(u32 crc, int options)
|
||||
void GSRendererHW::SetGameCRC(u32 crc, CRCHackLevel level)
|
||||
{
|
||||
GSRenderer::SetGameCRC(crc, options);
|
||||
GSRenderer::SetGameCRC(crc, level);
|
||||
|
||||
m_hacks.SetGameCRC(m_game);
|
||||
SetupCrcHack(level);
|
||||
|
||||
GSTextureReplacements::GameChanged();
|
||||
}
|
||||
|
@ -1830,7 +1830,7 @@ void GSRendererHW::Draw()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src))
|
||||
if (m_oi && !m_oi(*this, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src))
|
||||
{
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
return;
|
||||
|
@ -1945,10 +1945,8 @@ void GSRendererHW::Draw()
|
|||
|
||||
//
|
||||
|
||||
if (m_hacks.m_oo)
|
||||
{
|
||||
(this->*m_hacks.m_oo)();
|
||||
}
|
||||
if (m_oo)
|
||||
m_oo(*this);
|
||||
|
||||
if (GSConfig.DumpGSData)
|
||||
{
|
||||
|
@ -4128,49 +4126,6 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
|
|||
return true;
|
||||
}
|
||||
|
||||
// hacks
|
||||
|
||||
GSRendererHW::Hacks::Hacks()
|
||||
: m_oi_map(m_oi_list)
|
||||
, m_oo_map(m_oo_list)
|
||||
, m_oi(nullptr)
|
||||
, m_oo(nullptr)
|
||||
{
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BigMuthaTruckers, CRC::RegionCount, &GSRendererHW::OI_BigMuthaTruckers));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OI_DBZBTGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::DBZBT3, CRC::RegionCount, &GSRendererHW::OI_DBZBTGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::FFXII, CRC::EU, &GSRendererHW::OI_FFXII));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::FFX, CRC::RegionCount, &GSRendererHW::OI_FFX));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::MetalSlug6, CRC::RegionCount, &GSRendererHW::OI_MetalSlug6));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::RozenMaidenGebetGarden, CRC::RegionCount, &GSRendererHW::OI_RozenMaidenGebetGarden));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::SonicUnleashed, CRC::RegionCount, &GSRendererHW::OI_SonicUnleashed));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::ArTonelico2, CRC::RegionCount, &GSRendererHW::OI_ArTonelico2));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak2, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Jak3, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::JakX, CRC::RegionCount, &GSRendererHW::OI_JakGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BurnoutGames, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::Black, CRC::RegionCount, &GSRendererHW::OI_BurnoutGames));
|
||||
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::BurnoutGames, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
m_oo_list.push_back(HackEntry<OO_Ptr>(CRC::Black, CRC::RegionCount, &GSRendererHW::OO_BurnoutGames));
|
||||
}
|
||||
|
||||
void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
|
||||
{
|
||||
const u32 hash = (u32)((game.region << 24) | game.title);
|
||||
|
||||
m_oi = m_oi_map[hash];
|
||||
m_oo = m_oo_map[hash];
|
||||
|
||||
if (GSConfig.PointListPalette)
|
||||
{
|
||||
if (m_oi)
|
||||
Console.Warning("Overriding m_oi with PointListPalette");
|
||||
|
||||
m_oi = &GSRendererHW::OI_PointListPalette;
|
||||
}
|
||||
}
|
||||
|
||||
// Trick to do a fast clear on the GS
|
||||
// Set frame buffer pointer on the start of the buffer. Set depth buffer pointer on the half buffer
|
||||
// FB + depth write will fill the full buffer.
|
||||
|
@ -4415,421 +4370,55 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
|||
return true;
|
||||
}
|
||||
|
||||
// OI (others input?/implementation?) hacks replace current draw call
|
||||
|
||||
bool GSRendererHW::OI_BigMuthaTruckers(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
bool GSRendererHW::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// Rendering pattern:
|
||||
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
|
||||
// game needs to do a depth effect (so green channel to alpha),
|
||||
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
|
||||
// vertical resolution is half so only half is processed at once
|
||||
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
|
||||
|
||||
const GIFRegTEX0& Texture = m_context->TEX0;
|
||||
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = m_context->FRAME.FBW;
|
||||
Frame.TBP0 = m_context->FRAME.Block();
|
||||
|
||||
if (PRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Frame.TBP0 == 0x00a00 && Texture.PSM == PSM_PSMT8H && (m_r.y == 256 || m_r.y == 224))
|
||||
{
|
||||
// 224 ntsc, 256 pal.
|
||||
GL_INS("OI_BigMuthaTruckers half bottom offset");
|
||||
|
||||
const size_t count = m_vertex.next;
|
||||
GSVertex* v = &m_vertex.buff[0];
|
||||
const u16 offset = (u16)m_r.y * 16;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
v[i].V += offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!((m_r == GSVector4i(0, 0, 16, 16)).alltrue() || (m_r == GSVector4i(0, 0, 64, 64)).alltrue()))
|
||||
return true; // Only 16x16 or 64x64 draws.
|
||||
|
||||
// Sprite rendering
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
SwSpriteRender();
|
||||
|
||||
return false; // Skip current draw
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
static u32* video = nullptr;
|
||||
static size_t lines = 0;
|
||||
|
||||
if (lines == 0)
|
||||
{
|
||||
if (m_vt.m_primclass == GS_LINE_CLASS && (m_vertex.next == 448 * 2 || m_vertex.next == 512 * 2))
|
||||
{
|
||||
lines = m_vertex.next / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS)
|
||||
{
|
||||
if (m_vertex.next >= 16 * 512)
|
||||
{
|
||||
// incoming pixels are stored in columns, one column is 16x512, total res 448x512 or 448x454
|
||||
|
||||
if (!video)
|
||||
video = new u32[512 * 512];
|
||||
|
||||
const GSVertex* RESTRICT v = m_vertex.buff;
|
||||
const int ox = m_context->XYOFFSET.OFX - 8;
|
||||
const int oy = m_context->XYOFFSET.OFY - 8;
|
||||
|
||||
for (int i = (int)m_vertex.next; i > 0; i--, v++)
|
||||
{
|
||||
const int x = (v->XYZ.X - ox) >> 4;
|
||||
const int y = (v->XYZ.Y - oy) >> 4;
|
||||
|
||||
if (x < 0 || x >= 448 || y < 0 || y >= (int)lines)
|
||||
return false; // le sigh
|
||||
|
||||
video[(y << 8) + (y << 7) + (y << 6) + x] = v->RGBAQ.U32[0];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = 0;
|
||||
}
|
||||
}
|
||||
else if (m_vt.m_primclass == GS_LINE_CLASS)
|
||||
{
|
||||
if (m_vertex.next == lines * 2)
|
||||
{
|
||||
// normally, this step would copy the video onto screen with 512 texture mapped horizontal lines,
|
||||
// but we use the stored video data to create a new texture, and replace the lines with two triangles
|
||||
|
||||
g_gs_device->Recycle(t->m_texture);
|
||||
|
||||
t->m_texture = g_gs_device->CreateTexture(512, 512, 1, GSTexture::Format::Color);
|
||||
|
||||
t->m_texture->Update(GSVector4i(0, 0, 448, lines), video, 448 * 4);
|
||||
|
||||
m_vertex.buff[2] = m_vertex.buff[m_vertex.next - 2];
|
||||
m_vertex.buff[3] = m_vertex.buff[m_vertex.next - 1];
|
||||
|
||||
m_index.buff[0] = 0;
|
||||
m_index.buff[1] = 1;
|
||||
m_index.buff[2] = 2;
|
||||
m_index.buff[3] = 1;
|
||||
m_index.buff[4] = 2;
|
||||
m_index.buff[5] = 3;
|
||||
|
||||
m_vertex.head = m_vertex.tail = m_vertex.next = 4;
|
||||
m_index.tail = 6;
|
||||
|
||||
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GS_TRIANGLE_CLASS);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_FFX(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
const u32 FBP = m_context->FRAME.Block();
|
||||
const u32 ZBP = m_context->ZBUF.Block();
|
||||
const u32 TBP = m_context->TEX0.TBP0;
|
||||
|
||||
if ((FBP == 0x00d00 || FBP == 0x00000) && ZBP == 0x02100 && PRIM->TME && TBP == 0x01a00 && m_context->TEX0.PSM == PSM_PSMCT16S)
|
||||
{
|
||||
// random battle transition (z buffer written directly, clear it now)
|
||||
GL_INS("OI_FFX ZB clear");
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_MetalSlug6(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// missing red channel fix (looks alright in pcsx2 r5000+)
|
||||
|
||||
GSVertex* RESTRICT v = m_vertex.buff;
|
||||
|
||||
for (size_t i = m_vertex.next; i > 0; i--, v++)
|
||||
{
|
||||
const u32 c = v->RGBAQ.U32[0];
|
||||
|
||||
const u32 r = (c >> 0) & 0xff;
|
||||
const u32 g = (c >> 8) & 0xff;
|
||||
const u32 b = (c >> 16) & 0xff;
|
||||
|
||||
if (r == 0 && g != 0 && b != 0)
|
||||
{
|
||||
v->RGBAQ.U32[0] = (c & 0xffffff00) | ((g + b + 1) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, m_vt.m_primclass);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!PRIM->TME)
|
||||
{
|
||||
const u32 FBP = m_context->FRAME.Block();
|
||||
const u32 ZBP = m_context->ZBUF.Block();
|
||||
|
||||
if (FBP == 0x008c0 && ZBP == 0x01a40)
|
||||
{
|
||||
// frame buffer clear, atst = fail, afail = write z only, z buffer points to frame buffer
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
|
||||
TEX0.TBP0 = ZBP;
|
||||
TEX0.TBW = m_context->FRAME.FBW;
|
||||
TEX0.PSM = m_context->FRAME.PSM;
|
||||
|
||||
if (GSTextureCache::Target* tmp_rt = m_tc->LookupTarget(TEX0, GetTargetSize(), GSTextureCache::RenderTarget, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
||||
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (FBP == 0x00000 && m_context->ZBUF.Block() == 0x01180)
|
||||
{
|
||||
// z buffer clear, frame buffer now points to the z buffer (how can they be so clever?)
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
|
||||
TEX0.TBP0 = FBP;
|
||||
TEX0.TBW = m_context->FRAME.FBW;
|
||||
TEX0.PSM = m_context->ZBUF.PSM;
|
||||
|
||||
if (GSTextureCache::Target* tmp_ds = m_tc->LookupTarget(TEX0, GetTargetSize(), GSTextureCache::DepthStencil, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
||||
g_gs_device->ClearDepth(tmp_ds->m_texture);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// Rendering pattern is:
|
||||
// Save RG channel with a kind of a TS (replaced by a copy in this hack),
|
||||
// compute shadow in RG,
|
||||
// save result in alpha with a TS,
|
||||
// Restore RG channel that we previously copied to render shadows.
|
||||
|
||||
const GIFRegTEX0& Texture = m_context->TEX0;
|
||||
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = m_context->FRAME.FBW;
|
||||
Frame.TBP0 = m_context->FRAME.Block();
|
||||
Frame.PSM = m_context->FRAME.PSM;
|
||||
|
||||
if ((!PRIM->TME)
|
||||
|| (GSLocalMemory::m_psm[Texture.PSM].bpp != 16)
|
||||
|| (GSLocalMemory::m_psm[Frame.PSM].bpp != 16)
|
||||
|| (Texture.TBP0 == Frame.TBP0)
|
||||
|| (Frame.TBW != 16 && Texture.TBW != 16))
|
||||
return true;
|
||||
|
||||
GL_INS("OI_SonicUnleashed replace draw by a copy");
|
||||
|
||||
GSTextureCache::Target* src = m_tc->LookupTarget(Texture, GSVector2i(1, 1), GSTextureCache::RenderTarget, true);
|
||||
|
||||
const GSVector2i rt_size(rt->GetSize());
|
||||
const GSVector2i src_size(src->m_texture->GetSize());
|
||||
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
||||
|
||||
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
|
||||
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
|
||||
|
||||
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
const size_t n_vertices = m_vertex.next;
|
||||
const int w = m_r.width();
|
||||
const int h = m_r.height();
|
||||
const bool is_copy = !PRIM->ABE || (
|
||||
m_context->ALPHA.A == m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
|
||||
&& m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
|
||||
const size_t n_vertices = r.m_vertex.next;
|
||||
const int w = r.m_r.width();
|
||||
const int h = r.m_r.height();
|
||||
const bool is_copy = !r.PRIM->ABE || (
|
||||
r.m_context->ALPHA.A == r.m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
|
||||
&& r.m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
|
||||
);
|
||||
if (m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
|
||||
if (r.m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
|
||||
&& h <= 64 // Small draws.
|
||||
&& n_vertices <= 256 // Small draws.
|
||||
&& is_copy // Copy (no blending).
|
||||
&& !PRIM->TME // No texturing please.
|
||||
&& m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
|
||||
&& !PRIM->FGE // No FOG.
|
||||
&& !PRIM->AA1 // No antialiasing.
|
||||
&& !PRIM->FIX // Normal fragment value control.
|
||||
&& !m_env.DTHE.DTHE // No dithering.
|
||||
&& !m_context->TEST.ATE // No alpha test.
|
||||
&& !m_context->TEST.DATE // No destination alpha test.
|
||||
&& (!m_context->DepthRead() && !m_context->DepthWrite()) // No depth handling.
|
||||
&& !m_context->TEX0.CSM // No CLUT usage.
|
||||
&& !m_env.PABE.PABE // No PABE.
|
||||
&& m_context->FBA.FBA == 0 // No Alpha Correction.
|
||||
&& m_context->FRAME.FBMSK == 0 // No frame buffer masking.
|
||||
&& !r.PRIM->TME // No texturing please.
|
||||
&& r.m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
|
||||
&& !r.PRIM->FGE // No FOG.
|
||||
&& !r.PRIM->AA1 // No antialiasing.
|
||||
&& !r.PRIM->FIX // Normal fragment value control.
|
||||
&& !r.m_env.DTHE.DTHE // No dithering.
|
||||
&& !r.m_context->TEST.ATE // No alpha test.
|
||||
&& !r.m_context->TEST.DATE // No destination alpha test.
|
||||
&& (!r.m_context->DepthRead() && !r.m_context->DepthWrite()) // No depth handling.
|
||||
&& !r.m_context->TEX0.CSM // No CLUT usage.
|
||||
&& !r.m_env.PABE.PABE // No PABE.
|
||||
&& r.m_context->FBA.FBA == 0 // No Alpha Correction.
|
||||
&& r.m_context->FRAME.FBMSK == 0 // No frame buffer masking.
|
||||
)
|
||||
{
|
||||
const u32 FBP = m_context->FRAME.Block();
|
||||
const u32 FBW = m_context->FRAME.FBW;
|
||||
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", m_r.x, m_r.y, m_r.z, m_r.w, n_vertices, FBP, FBW);
|
||||
const GSVertex* RESTRICT v = m_vertex.buff;
|
||||
const int ox(m_context->XYOFFSET.OFX);
|
||||
const int oy(m_context->XYOFFSET.OFY);
|
||||
const u32 FBP = r.m_context->FRAME.Block();
|
||||
const u32 FBW = r.m_context->FRAME.FBW;
|
||||
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
|
||||
const GSVertex* RESTRICT v = r.m_vertex.buff;
|
||||
const int ox(r.m_context->XYOFFSET.OFX);
|
||||
const int oy(r.m_context->XYOFFSET.OFY);
|
||||
for (size_t i = 0; i < n_vertices; ++i)
|
||||
{
|
||||
const GSVertex& vi = v[i];
|
||||
const GIFRegXYZ& xyz = vi.XYZ;
|
||||
const int x = (int(xyz.X) - ox) / 16;
|
||||
const int y = (int(xyz.Y) - oy) / 16;
|
||||
if (x < m_r.x || x > m_r.z)
|
||||
if (x < r.m_r.x || x > r.m_r.z)
|
||||
continue;
|
||||
if (y < m_r.y || y > m_r.w)
|
||||
if (y < r.m_r.y || y > r.m_r.w)
|
||||
continue;
|
||||
const u32 c = vi.RGBAQ.U32[0];
|
||||
m_mem.WritePixel32(x, y, c, FBP, FBW);
|
||||
r.m_mem.WritePixel32(x, y, c, FBP, FBW);
|
||||
}
|
||||
m_tc->InvalidateVideoMem(m_context->offset.fb, m_r);
|
||||
r.m_tc->InvalidateVideoMem(r.m_context->offset.fb, r.m_r);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// world map clipping
|
||||
//
|
||||
// The bad draw call is a sprite rendering to clear the z buffer
|
||||
|
||||
/*
|
||||
Depth buffer description
|
||||
* width is 10 pages
|
||||
* texture/scissor size is 640x448
|
||||
* depth is 16 bits so it writes 70 (10w * 7h) pages of data.
|
||||
|
||||
following draw calls will use the buffer as 6 pages width with a scissor
|
||||
test of 384x672. So the above texture can be seen as a
|
||||
|
||||
* texture width: 6 pages * 64 pixels/page = 384
|
||||
* texture height: 70/6 pages * 64 pixels/page =746
|
||||
|
||||
So as you can see the GS issue a write of 640x448 but actually it
|
||||
expects to clean a 384x746 area. Ideally the fix will transform the
|
||||
buffer to adapt the page width properly.
|
||||
*/
|
||||
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
|
||||
if (m_vertex.next == 2 && !PRIM->TME && m_context->FRAME.FBW == 10 && v->XYZ.Z == 0 && m_context->TEST.ZTST == ZTST_ALWAYS)
|
||||
{
|
||||
GL_INS("OI_ArTonelico2");
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!(m_r == GSVector4i(0, 0, 16, 16)).alltrue())
|
||||
return true; // Only 16x16 draws.
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render 16x16 palette via CPU.
|
||||
SwSpriteRender();
|
||||
|
||||
return false; // Skip current draw.
|
||||
}
|
||||
|
||||
bool GSRendererHW::OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (!OI_PointListPalette(rt, ds, t))
|
||||
return false; // Render point list palette.
|
||||
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
return true;
|
||||
|
||||
if (!CanUseSwSpriteRender())
|
||||
return true;
|
||||
|
||||
// Render palette via CPU.
|
||||
SwSpriteRender();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OO (others output?) hacks: invalidate extra local memory after the draw call
|
||||
|
||||
void GSRendererHW::OO_BurnoutGames()
|
||||
{
|
||||
const GIFRegTEX0& TEX0 = m_context->TEX0;
|
||||
const GIFRegALPHA& ALPHA = m_context->ALPHA;
|
||||
const GIFRegFRAME& FRAME = m_context->FRAME;
|
||||
if (PRIM->PRIM == GS_SPRITE
|
||||
&& !PRIM->IIP
|
||||
&& PRIM->TME
|
||||
&& !PRIM->FGE
|
||||
&& PRIM->ABE
|
||||
&& !PRIM->AA1
|
||||
&& !PRIM->FST
|
||||
&& !PRIM->FIX
|
||||
&& TEX0.TBW == 16
|
||||
&& TEX0.TW == 10
|
||||
&& TEX0.TCC
|
||||
&& !TEX0.TFX
|
||||
&& TEX0.PSM == PSM_PSMT8
|
||||
&& TEX0.CPSM == PSM_PSMCT32
|
||||
&& !TEX0.CSM
|
||||
&& TEX0.TH == 8
|
||||
&& ALPHA.A == ALPHA.B
|
||||
&& ALPHA.D == 0
|
||||
&& FRAME.FBW == 16
|
||||
&& FRAME.PSM == PSM_PSMCT32)
|
||||
{
|
||||
// Readback clouds being rendered during level loading.
|
||||
// Later the alpha channel from the 32 bit frame buffer is used as an 8 bit indexed texture to draw
|
||||
// the clouds on top of the sky at each frame.
|
||||
// Burnout 3 PAL 50Hz: 0x3ba0 => 0x1e80.
|
||||
GL_INS("OO_BurnoutGames - Readback clouds renderered from TEX0.TBP0 = 0x%04x (TEX0.CBP = 0x%04x) to FBP = 0x%04x", TEX0.TBP0, TEX0.CBP, FRAME.Block());
|
||||
m_tc->InvalidateLocalMem(m_context->offset.fb, m_r);
|
||||
}
|
||||
}
|
||||
|
||||
// Can Upscale hacks: disable upscaling for some draw calls
|
||||
|
||||
// None required.
|
||||
|
|
|
@ -26,103 +26,41 @@ class GSRendererHW;
|
|||
MULTI_ISA_DEF(class GSRendererHWFunctions;)
|
||||
MULTI_ISA_DEF(void GSRendererHWPopulateFunctions(GSRendererHW& renderer);)
|
||||
|
||||
class GSHwHack;
|
||||
|
||||
struct GSFrameInfo
|
||||
{
|
||||
u32 FBP;
|
||||
u32 FPSM;
|
||||
u32 FBMSK;
|
||||
u32 ZBP;
|
||||
u32 ZMSK;
|
||||
u32 ZTST;
|
||||
u32 TME;
|
||||
u32 TBP0;
|
||||
u32 TPSM;
|
||||
};
|
||||
|
||||
class GSRendererHW : public GSRenderer
|
||||
{
|
||||
MULTI_ISA_FRIEND(GSRendererHWFunctions);
|
||||
friend GSHwHack;
|
||||
|
||||
public:
|
||||
static constexpr int MAX_FRAMEBUFFER_HEIGHT = 1280;
|
||||
|
||||
private:
|
||||
static constexpr float SSR_UV_TOLERANCE = 1.0f;
|
||||
|
||||
#pragma region hacks
|
||||
|
||||
typedef bool (GSRendererHW::*OI_Ptr)(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
typedef void (GSRendererHW::*OO_Ptr)();
|
||||
typedef bool (GSRendererHW::*CU_Ptr)();
|
||||
using GSC_Ptr = bool(*)(GSRendererHW& r, const GSFrameInfo& fi, int& skip); // GSC - Get Skip Count
|
||||
using OI_Ptr = bool(*)(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); // OI - Before draw
|
||||
using OO_Ptr = void(*)(GSRendererHW& r); // OO - After draw
|
||||
|
||||
// Require special argument
|
||||
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
|
||||
bool OI_GsMemClear(); // always on
|
||||
void OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds); // always on
|
||||
|
||||
bool OI_BigMuthaTruckers(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_FFXII(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_FFX(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_MetalSlug6(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_PointListPalette(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_JakGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_BurnoutGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
void OO_BurnoutGames();
|
||||
|
||||
class Hacks
|
||||
{
|
||||
template <class T>
|
||||
class HackEntry
|
||||
{
|
||||
public:
|
||||
CRC::Title title;
|
||||
CRC::Region region;
|
||||
T func;
|
||||
|
||||
HackEntry(CRC::Title t, CRC::Region r, T f)
|
||||
{
|
||||
title = t;
|
||||
region = r;
|
||||
func = f;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class FunctionMap : public GSFunctionMap<u32, T>
|
||||
{
|
||||
std::list<HackEntry<T>>& m_tbl;
|
||||
|
||||
T GetDefaultFunction(u32 key)
|
||||
{
|
||||
CRC::Title title = (CRC::Title)(key & 0xffffff);
|
||||
CRC::Region region = (CRC::Region)(key >> 24);
|
||||
|
||||
for (const auto& entry : m_tbl)
|
||||
{
|
||||
if (entry.title == title && (entry.region == CRC::RegionCount || entry.region == region))
|
||||
{
|
||||
return entry.func;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
FunctionMap(std::list<HackEntry<T>>& tbl)
|
||||
: m_tbl(tbl)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::list<HackEntry<OI_Ptr>> m_oi_list;
|
||||
std::list<HackEntry<OO_Ptr>> m_oo_list;
|
||||
|
||||
FunctionMap<OI_Ptr> m_oi_map;
|
||||
FunctionMap<OO_Ptr> m_oo_map;
|
||||
|
||||
public:
|
||||
OI_Ptr m_oi;
|
||||
OO_Ptr m_oo;
|
||||
|
||||
Hacks();
|
||||
|
||||
void SetGameCRC(const CRC::Game& game);
|
||||
|
||||
} m_hacks;
|
||||
|
||||
#pragma endregion
|
||||
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
u16 Interpolate_UV(float alpha, int t0, int t1);
|
||||
float alpha0(int L, int X0, int X1);
|
||||
|
@ -155,6 +93,16 @@ private:
|
|||
GSVector4i m_r;
|
||||
GSTextureCache::Source* m_src;
|
||||
|
||||
// CRC Hacks
|
||||
bool IsBadFrame();
|
||||
void SetupCrcHack(CRCHackLevel level);
|
||||
|
||||
GSC_Ptr m_gsc = nullptr;
|
||||
OI_Ptr m_oi = nullptr;
|
||||
OO_Ptr m_oo = nullptr;
|
||||
int m_skip = 0;
|
||||
int m_skip_offset = 0;
|
||||
|
||||
bool m_reset;
|
||||
bool m_tex_is_fb;
|
||||
bool m_channel_shuffle;
|
||||
|
@ -180,7 +128,7 @@ public:
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
void SetGameCRC(u32 crc, int options) override;
|
||||
void SetGameCRC(u32 crc, CRCHackLevel level) override;
|
||||
bool CanUpscale() override;
|
||||
float GetUpscaleMultiplier() override;
|
||||
void Lines2Sprites();
|
||||
|
|
|
@ -276,7 +276,7 @@ bool SysMtgsThread::TryOpenGS()
|
|||
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
||||
return false;
|
||||
|
||||
GSsetGameCRC(ElfCRC, 0);
|
||||
GSsetGameCRC(ElfCRC);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -511,7 +511,7 @@ void SysMtgsThread::MainLoop()
|
|||
break;
|
||||
|
||||
case GS_RINGTYPE_CRC:
|
||||
GSsetGameCRC(tag.data[0], 0);
|
||||
GSsetGameCRC(tag.data[0]);
|
||||
break;
|
||||
|
||||
case GS_RINGTYPE_INIT_AND_READ_FIFO:
|
||||
|
|
|
@ -555,6 +555,7 @@
|
|||
<ClInclude Include="Frontend\imgui_impl_dx12.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_opengl3.h" />
|
||||
<ClInclude Include="Frontend\imgui_impl_vulkan.h" />
|
||||
<ClInclude Include="GS\Renderers\HW\GSHwHack.h" />
|
||||
<ClInclude Include="INISettingsInterface.h" />
|
||||
<ClInclude Include="Frontend\InputManager.h" />
|
||||
<ClInclude Include="Frontend\InputSource.h" />
|
||||
|
|
|
@ -2342,6 +2342,9 @@
|
|||
<ClInclude Include="SPU2\Dma.h">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\HW\GSHwHack.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Hardware</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuildStep Include="rdebug\deci2.h">
|
||||
|
|
Loading…
Reference in New Issue