Skip to content

Commit

Permalink
GS: Add Pre-Round Sprite hack
Browse files Browse the repository at this point in the history
This attempts to preproduce hardware behaviour, but falls down in a bunch of cases, hence the hack.
  • Loading branch information
refractionpcsx2 committed Sep 24, 2023
1 parent 8b84b04 commit 8cb832b
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 4 deletions.
1 change: 1 addition & 0 deletions pcsx2-qt/Settings/GraphicsSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.screenshotFormat, "EmuCore/GS", "ScreenshotFormat", static_cast<int>(GSScreenshotFormat::PNG));
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.screenshotQuality, "EmuCore/GS", "ScreenshotQuality", 50);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PreRoundSprites, "EmuCore/GS", "preround_sprites", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cropLeft, "EmuCore/GS", "CropLeft", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cropTop, "EmuCore/GS", "CropTop", 0);
Expand Down
10 changes: 10 additions & 0 deletions pcsx2-qt/Settings/GraphicsSettingsWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,16 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="PreRoundSprites">
<property name="toolTip">
<string>Attempts to pre-round sprite texel coordinates to resolve rounding issues. Helpful for games such as Beyond Good and Evil, and Manhunt</string>
</property>
<property name="text">
<string>Pre-Round Sprites</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
Expand Down
1 change: 1 addition & 0 deletions pcsx2/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ struct Pcsx2Config
AutoFlushSW : 1,
PreloadFrameWithGSData : 1,
Mipmap : 1,
PreRoundSprites : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
Expand Down
142 changes: 138 additions & 4 deletions pcsx2/GS/GSState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ void GSState::DumpVertices(const std::string& filename)
file << uv_U << DEL << uv_V;
}
else
file << v.ST.S << DEL << v.ST.T << DEL << v.RGBAQ.Q;
file << v.ST.S << "(" << *(u32*)&v.ST.S << ")" << DEL << v.ST.T << "(" << *(u32*)&v.ST.T << ")" << DEL << v.RGBAQ.Q << "(" << *(u32*)&v.RGBAQ.Q << ")";

file << std::endl;
}
Expand Down Expand Up @@ -1580,7 +1580,18 @@ inline bool GSState::TestDrawChanged()

return false;
}

u32 GSState::CalcMask(int exp, int max_exp)
{
int amount = 9 + (max_exp - exp);
u32 result = 0x1ff;

for (int i = 9; i < amount; i++)
{
result |= 1 << i;
}
result &= 0x7fffff;
return result;
}
void GSState::FlushPrim()
{
if (m_index.tail > 0)
Expand Down Expand Up @@ -1644,6 +1655,131 @@ void GSState::FlushPrim()
ASSERT((int)unused < GSUtil::GetVertexCount(PRIM->PRIM));
}

// Texel coordinate rounding
// Helps Beyond Good & Evil (water) and Manhunt (lights shining through objects).
// Dark Cloud 2 also benefits in some ways but is broken in others.
// Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers.
// This is *NOT* 100% safe, some games hate it (Gran Turismo 4's post processing for example).
// I'm sure some of this is wrong, so as of now it serves as a hack fix.
if (m_env.PRIM.TME &&/* !m_context->TEX1.MMAG && !(m_context->TEX1.MMIN & 1) &&*/ GSConfig.PreRoundSprites)
{
if (m_env.PRIM.FST) // UV's
{
// UV's on sprites only alter the final UV, I believe the problem is we're drawing too much (drawing stops earlier than we do)
// But this fixes Beyond Good adn Evil water and Dark Cloud 2's UI
if (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS)
{
for (u32 i = 0; i < m_index.tail; i+=2)
{
GSVertex* v1 = &m_vertex.buff[m_index.buff[i]];
GSVertex* v2 = &m_vertex.buff[m_index.buff[i+1]];

GSVertex* vu1;
GSVertex* vu2;
GSVertex* vv1;
GSVertex* vv2;
if (v1->U > v2->U)
{
vu2 = v1;
vu1 = v2;
}
else
{
vu2 = v2;
vu1 = v1;
}

if (v1->V > v2->V)
{
vv2 = v1;
vv1 = v2;
}
else
{
vv2 = v2;
vv1 = v1;
}

GSVector2 pos_range(vu2->XYZ.X - vu1->XYZ.X, vv2->XYZ.Y - vv1->XYZ.Y);
const GSVector2 uv_range(vu2->U - vu1->U, vv2->V - vv1->V);
if (pos_range.x == 0)
pos_range.x = 1;
if (pos_range.y == 0)
pos_range.y = 1;
const GSVector2 grad(uv_range / pos_range);
bool isclamp_w = m_context->CLAMP.WMS > 0 && m_context->CLAMP.WMS < 3;
bool isclamp_h = m_context->CLAMP.WMT > 0 && m_context->CLAMP.WMT < 3;
int max_w = (m_context->CLAMP.WMS == 2) ? m_context->CLAMP.MAXU : ((1 << m_context->TEX0.TW) - 1);
int max_h = (m_context->CLAMP.WMT == 2) ? m_context->CLAMP.MAXV : ((1 << m_context->TEX0.TH) - 1);
int width = vu2->U >> 4;
int height = vv2->V >> 4;

if (m_context->CLAMP.WMS == 3)
width = (width & m_context->CLAMP.MINU) | m_context->CLAMP.MAXU;

if (m_context->CLAMP.WMT == 3)
height = (height & m_context->CLAMP.MINV) | m_context->CLAMP.MAXV;

if ((isclamp_w && width <= max_w && grad.x != 1.0f) || !isclamp_w)
{
if (vu2->U >= 0x1 && (!(vu2->U & 0xf) || grad.x <= 1.0f))
{
vu2->U = (vu2->U - 1);
/*if (!isclamp_w && vu2->XYZ.X < m_context->scissor.ofex.z)
vu2->XYZ.X -= 1;*/
}
}
else
vu2->U &= ~0xf;

// Dark Cloud 2 tries to address wider than the TBW when in CLAMP mode (maybe some weird behaviour in HW?)
if ((vu2->U >> 4) > (int)(m_context->TEX0.TBW * 64) && isclamp_w && (vu2->U >> 4) - 8 <= (int)(m_context->TEX0.TBW * 64))
{
vu2->U = (m_context->TEX0.TBW * 64) << 4;
}

if ((isclamp_h && height <= max_h && grad.y != 1.0f) || !isclamp_h)
{
if (vv2->V >= 0x1 && (!(vv2->V & 0xf) || grad.y <= 1.0f))
{
vv2->V = (vv2->V - 1);
/*if (!isclamp_h && vv2->XYZ.Y < m_context->scissor.ofex.w)
vv2->XYZ.Y -= 1;*/
}
}
else
vv2->V &= ~0xf;
}
}
}
else if(!m_env.PRIM.FST)
{
// ST's have the lowest 8 bits rounding down (Manhunt lighting)
for (u32 i = 0; i < m_vertex.tail; i++)
{
// Don't worry, I hate me too. If there's a modern way to do bit manipulation on floats, I'd love to hear it!
GSVertex* v = &m_vertex.buff[i];
u32 S = *(u32*)&v->ST.S;
u32 T = *(u32*)&v->ST.T;
u32 Q = *(u32*)&v->RGBAQ.Q;
int expS = (S >> 23) & 0xff;
int expT = (T >> 23) & 0xff;
int expQ = (Q >> 23) & 0xff;
int max_exp = std::max(expS, expQ);

u32 mask = CalcMask(expS, max_exp);
S &= ~mask;
v->ST.S = *(float*)&S;
max_exp = std::max(expT, expQ);
mask = CalcMask(expT, max_exp);
T &= ~mask;
v->ST.T = *(float*)&T;
Q &= ~0xff;
v->RGBAQ.Q = *(float*)&Q;
}
}
}

// If the PSM format of Z is invalid, but it is masked (no write) and ZTST is set to ALWAYS pass (no test, just allow)
// we can ignore the Z format, since it won't be used in the draw (Star Ocean 3 transitions)
#ifdef PCSX2_DEVBUILD
Expand Down Expand Up @@ -1671,7 +1807,6 @@ void GSState::FlushPrim()
if (unused > 0)
{
memcpy(m_vertex.buff, buff, sizeof(GSVertex) * unused);

m_vertex.tail = unused;
m_vertex.next = next > head ? next - head : 0;

Expand Down Expand Up @@ -3336,7 +3471,6 @@ __forceinline void GSState::VertexKick(u32 skip)
const GSVector4i new_v1(m_v.m[1]);

GSVector4i* RESTRICT tailptr = (GSVector4i*)&m_vertex.buff[tail];

tailptr[0] = new_v0;
tailptr[1] = new_v1;

Expand Down
1 change: 1 addition & 0 deletions pcsx2/GS/GSState.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ class GSState : public GSAlignedClass<32>
virtual void UpdateSettings(const Pcsx2Config::GSOptions& old_config);

void Flush(GSFlushReason reason);
u32 CalcMask(int exp, int max_exp);
void FlushPrim();
bool TestDrawChanged();
void FlushWrite();
Expand Down
6 changes: 6 additions & 0 deletions pcsx2/GameDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ static const char* s_gs_hw_fix_names[] = {
"estimateTextureRegion",
"PCRTCOffsets",
"PCRTCOverscan",
"preRoundSprites",
"mipmap",
"trilinearFiltering",
"skipDrawStart",
Expand Down Expand Up @@ -394,6 +395,7 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
case GSHWFixId::Deinterlace:
case GSHWFixId::Mipmap:
case GSHWFixId::TexturePreloading:
case GSHWFixId::PreRoundSprites:
case GSHWFixId::TrilinearFiltering:
case GSHWFixId::MinimumBlendingLevel:
case GSHWFixId::MaximumBlendingLevel:
Expand Down Expand Up @@ -739,6 +741,10 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.PCRTCOverscan = (value > 0);
break;

case GSHWFixId::PreRoundSprites:
config.PreRoundSprites = (value > 0);
break;

case GSHWFixId::Mipmap:
{
if (value >= 0 && value <= static_cast<int>(HWMipmapLevel::Full))
Expand Down
1 change: 1 addition & 0 deletions pcsx2/GameDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ namespace GameDatabaseSchema
EstimateTextureRegion,
PCRTCOffsets,
PCRTCOverscan,
PreRoundSprites,

// integer settings
Mipmap,
Expand Down
2 changes: 2 additions & 0 deletions pcsx2/Pcsx2Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ Pcsx2Config::GSOptions::GSOptions()
AutoFlushSW = true;
PreloadFrameWithGSData = false;
Mipmap = true;
PreRoundSprites = false;

ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
Expand Down Expand Up @@ -734,6 +735,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
GSSettingBool(OsdShowSettings);
GSSettingBool(OsdShowInputs);
GSSettingBool(OsdShowFrameTimes);
GSSettingBoolEx(PreRoundSprites, "preround_sprites");

GSSettingBool(HWSpinGPUForReadbacks);
GSSettingBool(HWSpinCPUForReadbacks);
Expand Down

0 comments on commit 8cb832b

Please sign in to comment.