Skip to content

Commit 7f89851

Browse files
committed
GS: Clamp huge/infinite/nan values in vertex trace.
1 parent df7646f commit 7f89851

File tree

3 files changed

+88
-49
lines changed

3 files changed

+88
-49
lines changed

pcsx2/GS/GSState.cpp

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,48 +1677,9 @@ void GSState::FlushPrim()
16771677

16781678
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
16791679

1680-
// Texel coordinate rounding
1681-
// Helps Manhunt (lights shining through objects).
1682-
// Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers.
1683-
// Sometimes hardware doesn't get affected, likely due to the difference in how GPU's handle textures (Persona minimap).
16841680
if (PRIM->TME && (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS || m_vt.m_eq.z))
16851681
{
1686-
if (!PRIM->FST) // STQ's
1687-
{
1688-
const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS;
1689-
// ST's have the lowest 9 bits (or greater depending on exponent difference) rounding down (from hardware tests).
1690-
for (int i = m_index.tail - 1; i >= 0; i--)
1691-
{
1692-
GSVertex* v = &m_vertex.buff[m_index.buff[i]];
1693-
1694-
// Only Q on the second vertex is valid
1695-
if (!(i & 1) && is_sprite)
1696-
v->RGBAQ.Q = m_vertex.buff[m_index.buff[i + 1]].RGBAQ.Q;
1697-
1698-
int T = std::bit_cast<int>(v->ST.T);
1699-
int Q = std::bit_cast<int>(v->RGBAQ.Q);
1700-
int S = std::bit_cast<int>(v->ST.S);
1701-
const int expS = (S >> 23) & 0xff;
1702-
const int expT = (T >> 23) & 0xff;
1703-
const int expQ = (Q >> 23) & 0xff;
1704-
int max_exp = std::max(expS, expQ);
1705-
1706-
u32 mask = CalcMask(expS, max_exp);
1707-
S &= ~mask;
1708-
v->ST.S = std::bit_cast<float>(S);
1709-
max_exp = std::max(expT, expQ);
1710-
mask = CalcMask(expT, max_exp);
1711-
T &= ~mask;
1712-
v->ST.T = std::bit_cast<float>(T);
1713-
Q &= ~0xff;
1714-
1715-
if (!is_sprite || (i & 1))
1716-
v->RGBAQ.Q = std::bit_cast<float>(Q);
1717-
1718-
m_vt.m_min.t.x = std::min(m_vt.m_min.t.x, (v->ST.S / v->RGBAQ.Q) * (1 << m_context->TEX0.TW));
1719-
m_vt.m_min.t.y = std::min(m_vt.m_min.t.y, (v->ST.T / v->RGBAQ.Q) * (1 << m_context->TEX0.TH));
1720-
}
1721-
}
1682+
TexelCoordinateRounding();
17221683
}
17231684

17241685
// Skip draw if Z test is enabled, but set to fail all pixels.
@@ -3831,8 +3792,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
38313792

38323793
u8 uses_border = 0;
38333794

3834-
if (m_vt.m_max.t.x >= FLT_MAX || m_vt.m_min.t.x <= -FLT_MAX ||
3835-
m_vt.m_max.t.y >= FLT_MAX || m_vt.m_min.t.y <= -FLT_MAX)
3795+
if (m_vt.m_max.t.x >= 2047.0f || m_vt.m_min.t.x <= -2047.0f ||
3796+
m_vt.m_max.t.y >= 2047.0f || m_vt.m_min.t.y <= -2047.0f)
38363797
{
38373798
// If any of the min/max values are +-FLT_MAX we can't rely on them
38383799
// so just assume full texture.
@@ -4009,6 +3970,61 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
40093970
return { vr, uses_border };
40103971
}
40113972

3973+
// Texel coordinate rounding
3974+
// Helps Manhunt (lights shining through objects).
3975+
// Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers.
3976+
// Sometimes hardware doesn't get affected, likely due to the difference in how GPU's handle textures (Persona minimap).
3977+
void GSState::TexelCoordinateRounding()
3978+
{
3979+
if (!PRIM->FST) // STQ's
3980+
{
3981+
const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS;
3982+
// ST's have the lowest 9 bits (or greater depending on exponent difference) rounding down (from hardware tests).
3983+
for (int i = m_index.tail - 1; i >= 0; i--)
3984+
{
3985+
GSVertex* v = &m_vertex.buff[m_index.buff[i]];
3986+
3987+
// Only Q on the second vertex is valid
3988+
if (!(i & 1) && is_sprite)
3989+
v->RGBAQ.Q = m_vertex.buff[m_index.buff[i + 1]].RGBAQ.Q;
3990+
3991+
int T = std::bit_cast<int>(v->ST.T);
3992+
int Q = std::bit_cast<int>(v->RGBAQ.Q);
3993+
int S = std::bit_cast<int>(v->ST.S);
3994+
const int expS = (S >> 23) & 0xff;
3995+
const int expT = (T >> 23) & 0xff;
3996+
const int expQ = (Q >> 23) & 0xff;
3997+
int max_exp = std::max(expS, expQ);
3998+
3999+
u32 mask = CalcMask(expS, max_exp);
4000+
S &= ~mask;
4001+
v->ST.S = std::bit_cast<float>(S);
4002+
max_exp = std::max(expT, expQ);
4003+
mask = CalcMask(expT, max_exp);
4004+
T &= ~mask;
4005+
v->ST.T = std::bit_cast<float>(T);
4006+
Q &= ~0xff;
4007+
4008+
if (!is_sprite || (i & 1))
4009+
v->RGBAQ.Q = std::bit_cast<float>(Q);
4010+
4011+
float U = (v->ST.S / v->RGBAQ.Q) * (1 << m_context->TEX0.TW);
4012+
float V = (v->ST.T / v->RGBAQ.Q) * (1 << m_context->TEX0.TH);
4013+
4014+
m_vt.m_min.t.x = std::isnan(U) ? m_vt.m_min.t.x : std::min(m_vt.m_min.t.x, U);
4015+
m_vt.m_min.t.y = std::isnan(V) ? m_vt.m_min.t.y : std::min(m_vt.m_min.t.y, V);
4016+
m_vt.m_max.t.x = std::isnan(U) ? m_vt.m_max.t.x : std::max(m_vt.m_max.t.x, U);
4017+
m_vt.m_max.t.y = std::isnan(V) ? m_vt.m_max.t.y : std::max(m_vt.m_max.t.y, V);
4018+
}
4019+
}
4020+
4021+
// Clamp the min/max UV values to the min/max valid UV values.
4022+
// This is needed in certain cases where buggy GS input results
4023+
// in huge floating points values for ST.
4024+
m_vt.m_min.t = m_vt.m_min.t.min(GSVector4(2047.0f)).max(GSVector4(-2047.0f)).xyzw(m_vt.m_min.t);
4025+
m_vt.m_max.t = m_vt.m_max.t.min(GSVector4(2047.0f)).max(GSVector4(-2047.0f)).xyzw(m_vt.m_max.t);
4026+
}
4027+
40124028
void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
40134029
{
40144030
if (m_vt.m_alpha.valid && tex_alpha_min == 0 && tex_alpha_max == 255)

pcsx2/GS/GSState.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ class GSState : public GSAlignedClass<32>
190190
bool IsCoverageAlpha();
191191
void CalcAlphaMinMax(const int tex_min, const int tex_max);
192192
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
193+
void TexelCoordinateRounding();
193194

194195
public:
195196
struct GSUploadQueue

pcsx2/GS/Renderers/Common/GSVertexTraceFMM.cpp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,36 @@ void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u
138138

139139
stq0 = st.xyww(primclass == GS_SPRITE_CLASS ? stq1 : stq0);
140140
stq1 = st.zwww(stq1);
141-
142-
tmin = tmin.min(stq0.min(stq1));
143-
tmax = tmax.max(stq0.max(stq1));
141+
142+
// Check for NaNs
143+
GSVector4 temp_min = stq0.min(stq1);
144+
GSVector4 temp_max = stq0.max(stq1);
145+
temp_min.x = std::isnan(temp_min.x) ? s_minmax.x : temp_min.x;
146+
temp_min.y = std::isnan(temp_min.y) ? s_minmax.x : temp_min.y;
147+
temp_max.x = std::isnan(temp_max.x) ? s_minmax.y : temp_max.x;
148+
temp_max.y = std::isnan(temp_max.y) ? s_minmax.y : temp_max.y;
149+
150+
tmin = tmin.min(temp_min);
151+
tmax = tmax.max(temp_max);
144152
}
145153
else
146154
{
147155
GSVector4i uv0(v0.m[1]);
148156
GSVector4i uv1(v1.m[1]);
149157

150-
GSVector4 st0 = GSVector4(uv0.uph16()).xyxy();
151-
GSVector4 st1 = GSVector4(uv1.uph16()).xyxy();
158+
GSVector4 st0 = GSVector4(uv0.uph16());
159+
GSVector4 st1 = GSVector4(uv1.uph16());
160+
161+
// Check for NaNs
162+
GSVector4 temp_min = st0.min(st1);
163+
GSVector4 temp_max = st0.max(st1);
164+
temp_min.x = std::isnan(temp_min.x) ? s_minmax.x : temp_min.x;
165+
temp_min.y = std::isnan(temp_min.y) ? s_minmax.x : temp_min.y;
166+
temp_max.x = std::isnan(temp_max.x) ? s_minmax.y : temp_max.x;
167+
temp_max.y = std::isnan(temp_max.y) ? s_minmax.y : temp_max.y;
152168

153-
tmin = tmin.min(st0.min(st1));
154-
tmax = tmax.max(st0.max(st1));
169+
tmin = tmin.min(temp_min.xyxy());
170+
tmax = tmax.max(temp_max.xyxy());
155171
}
156172
}
157173

@@ -246,6 +262,12 @@ void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u
246262

247263
vt.m_min.t = tmin * s;
248264
vt.m_max.t = tmax * s;
265+
266+
// Clamp the min/max UV values to the min/max valid UV values.
267+
// This is needed in certain cases where buggy GS input results
268+
// in huge floating points values for ST.
269+
vt.m_min.t = vt.m_min.t.min(GSVector4(2047.0f)).max(GSVector4(-2047.0f)).xyzw(vt.m_min.t);
270+
vt.m_max.t = vt.m_max.t.min(GSVector4(2047.0f)).max(GSVector4(-2047.0f)).xyzw(vt.m_max.t);
249271
}
250272
else
251273
{

0 commit comments

Comments
 (0)