diff --git a/Source/gs/GSH_OpenGL/GSH_OpenGL.cpp b/Source/gs/GSH_OpenGL/GSH_OpenGL.cpp index 7eb9460f41..fd22247ea4 100644 --- a/Source/gs/GSH_OpenGL/GSH_OpenGL.cpp +++ b/Source/gs/GSH_OpenGL/GSH_OpenGL.cpp @@ -327,6 +327,14 @@ void CGSH_OpenGL::CheckExtensions() } } +void CGSH_OpenGL::SetSpriteRoundingHackEnabled(bool enabled) +{ + SendGSCall( + [this, enabled]() { + m_spriteRoundingHackEnabled = enabled; + }); +} + Framework::OpenGl::CBuffer CGSH_OpenGL::GeneratePresentVertexBuffer() { auto buffer = Framework::OpenGl::CBuffer::Create(); @@ -1642,6 +1650,19 @@ void CGSH_OpenGL::Prim_Sprite() nY1 -= m_nPrimOfsY; nY2 -= m_nPrimOfsY; + // Sprite boundary rounding hack: fixes vertical line seams between + // adjacent sprites (seen in Namco tekken4/tekken5/taiko titles). + // Opt-in only, enabled via SetSpriteRoundingHackEnabled() from the + // arcade boot path (ArcadeUtils::BootArcadeMachine) so it doesn't + // affect unrelated titles. + if(m_spriteRoundingHackEnabled) + { + nX1 = round(nX1); + nY1 = round(nY1); + nX2 = round(nX2); + nY2 = round(nY2); + } + float nS[2] = {0, 0}; float nT[2] = {0, 0}; diff --git a/Source/gs/GSH_OpenGL/GSH_OpenGL.h b/Source/gs/GSH_OpenGL/GSH_OpenGL.h index 290deb702a..26835bb584 100644 --- a/Source/gs/GSH_OpenGL/GSH_OpenGL.h +++ b/Source/gs/GSH_OpenGL/GSH_OpenGL.h @@ -54,6 +54,8 @@ class CGSH_OpenGL : public CGSHandler, public CGsDebuggerInterface const VERTEX* GetInputVertices() const override; + void SetSpriteRoundingHackEnabled(bool enabled) override; + protected: void PalCache_Flush(); void LoadPreferences(); @@ -400,6 +402,7 @@ class CGSH_OpenGL : public CGSHandler, public CGsDebuggerInterface bool m_forceBilinearTextures = false; unsigned int m_fbScale = 1; bool m_multisampleEnabled = false; + bool m_spriteRoundingHackEnabled = false; bool m_depthTestingEnabled = true; bool m_alphaBlendingEnabled = true; bool m_alphaTestingEnabled = true; diff --git a/Source/gs/GSH_OpenGLWin32/GSH_OpenGLWin32.cpp b/Source/gs/GSH_OpenGLWin32/GSH_OpenGLWin32.cpp index dc34bcbb55..4d5c9926c1 100644 --- a/Source/gs/GSH_OpenGLWin32/GSH_OpenGLWin32.cpp +++ b/Source/gs/GSH_OpenGLWin32/GSH_OpenGLWin32.cpp @@ -69,7 +69,7 @@ void CGSH_OpenGLWin32::InitializeImpl() if(wglSwapIntervalEXT) { - wglSwapIntervalEXT(-1); + wglSwapIntervalEXT(1); } CGSH_OpenGL::InitializeImpl(); diff --git a/Source/gs/GSHandler.h b/Source/gs/GSHandler.h index 66bfe59a65..e698d190ac 100644 --- a/Source/gs/GSHandler.h +++ b/Source/gs/GSHandler.h @@ -824,6 +824,13 @@ class CGSHandler static void RegisterPreferences(); void NotifyPreferencesChanged(); + // Default no-op. Only overridden by renderer backends (e.g. CGSH_OpenGL) + // that implement the sprite boundary rounding hack for specific arcade + // titles (tekken4/tekken5/taiko series) to fix vertical line seams. + virtual void SetSpriteRoundingHackEnabled(bool enabled) + { + } + void SetIntc(CINTC*); void Reset(); virtual void SetPresentationParams(const PRESENTATION_PARAMS&); diff --git a/Source/ui_shared/ArcadeUtils.cpp b/Source/ui_shared/ArcadeUtils.cpp index 9ab3f8b6ea..3c03bb75b3 100644 --- a/Source/ui_shared/ArcadeUtils.cpp +++ b/Source/ui_shared/ArcadeUtils.cpp @@ -282,10 +282,8 @@ void ArcadeUtils::RegisterArcadeMachines() return; } - //This set is used to track inactive arcadedefs we might have in the database std::set inactiveArcadeDefs; - //Collect all arcadedef files we have registered in the database { auto arcadeBootables = BootablesDb::CClient::GetInstance().GetBootables(BootablesDb::CClient::SORT_METHOD_ARCADE); for(const auto& bootable : arcadeBootables) @@ -298,7 +296,6 @@ void ArcadeUtils::RegisterArcadeMachines() { auto arcadeDefPath = entry.path(); auto arcadeDefFilename = arcadeDefPath.filename(); - //Remove this arcadedef from the inactive set inactiveArcadeDefs.erase(arcadeDefFilename); try { @@ -313,7 +310,6 @@ void ArcadeUtils::RegisterArcadeMachines() } } - //Prune any remaining arcadedef for(const auto& inactiveArcadeDef : inactiveArcadeDefs) { BootablesDb::CClient::GetInstance().UnregisterBootable(inactiveArcadeDef); @@ -322,14 +318,12 @@ void ArcadeUtils::RegisterArcadeMachines() static CNamcoSys246Driver g_sys246Driver; static CNamcoSys147Driver g_sys147Driver; -// clang-format off static CArcadeDriver* g_drivers[] = -{ - nullptr, - &g_sys246Driver, - &g_sys147Driver, + { + nullptr, + &g_sys246Driver, + &g_sys147Driver, }; -// clang-format on void ArcadeUtils::BootArcadeMachine(CPS2VM* virtualMachine, const fs::path& arcadeDefFilename) { @@ -355,6 +349,19 @@ void ArcadeUtils::BootArcadeMachine(CPS2VM* virtualMachine, const fs::path& arca driver->PrepareEnvironment(virtualMachine, def); driver->Launch(virtualMachine, def); + { + bool spriteRoundingHackEnabled = + (def.id.rfind("tekken4", 0) == 0) || + (def.id.rfind("tekken5", 0) == 0) || + (def.id.rfind("soulclb3", 0) == 0) || + (def.id.rfind("taiko", 0) == 0); + + if(auto gsHandler = virtualMachine->GetGSHandler()) + { + gsHandler->SetSpriteRoundingHackEnabled(spriteRoundingHackEnabled); + } + } + ApplyPatchesFromArcadeDefinition(virtualMachine, def); ApplyIdleLoopBlocksFromArcadeDefinition(virtualMachine, def);