Skip to content
21 changes: 21 additions & 0 deletions Source/gs/GSH_OpenGL/GSH_OpenGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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};

Expand Down
3 changes: 3 additions & 0 deletions Source/gs/GSH_OpenGL/GSH_OpenGL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Source/gs/GSH_OpenGLWin32/GSH_OpenGLWin32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void CGSH_OpenGLWin32::InitializeImpl()

if(wglSwapIntervalEXT)
{
wglSwapIntervalEXT(-1);
wglSwapIntervalEXT(1);
}

CGSH_OpenGL::InitializeImpl();
Expand Down
7 changes: 7 additions & 0 deletions Source/gs/GSHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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&);
Expand Down
27 changes: 17 additions & 10 deletions Source/ui_shared/ArcadeUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,8 @@ void ArcadeUtils::RegisterArcadeMachines()
return;
}

//This set is used to track inactive arcadedefs we might have in the database
std::set<fs::path> 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)
Expand All @@ -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
{
Expand All @@ -313,7 +310,6 @@ void ArcadeUtils::RegisterArcadeMachines()
}
}

//Prune any remaining arcadedef
for(const auto& inactiveArcadeDef : inactiveArcadeDefs)
{
BootablesDb::CClient::GetInstance().UnregisterBootable(inactiveArcadeDef);
Expand All @@ -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)
{
Expand All @@ -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);

Expand Down
Loading