Skip to content

Commit 1f974c9

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

File tree

2 files changed

+90
-52
lines changed

2 files changed

+90
-52
lines changed

pcsx2/GS/GSState.cpp

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,49 +1677,7 @@ 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).
1684-
if (PRIM->TME && (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS || m_vt.m_eq.z))
1685-
{
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-
}
1722-
}
1680+
TexelCoordinateRounding();
17231681

17241682
// Skip draw if Z test is enabled, but set to fail all pixels.
17251683
const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER);
@@ -3831,8 +3789,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
38313789

38323790
u8 uses_border = 0;
38333791

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)
3792+
if (m_vt.m_max.t.x >= 2047.0f || m_vt.m_min.t.x <= -2047.0f ||
3793+
m_vt.m_max.t.y >= 2047.0f || m_vt.m_min.t.y <= -2047.0f)
38363794
{
38373795
// If any of the min/max values are +-FLT_MAX we can't rely on them
38383796
// so just assume full texture.
@@ -4009,6 +3967,64 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
40093967
return { vr, uses_border };
40103968
}
40113969

3970+
// Texel coordinate rounding
3971+
// Helps Manhunt (lights shining through objects).
3972+
// Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers.
3973+
// Sometimes hardware doesn't get affected, likely due to the difference in how GPU's handle textures (Persona minimap).
3974+
void GSState::TexelCoordinateRounding()
3975+
{
3976+
if (PRIM->TME && (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS || m_vt.m_eq.z))
3977+
{
3978+
if (!PRIM->FST) // STQ's
3979+
{
3980+
const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS;
3981+
// ST's have the lowest 9 bits (or greater depending on exponent difference) rounding down (from hardware tests).
3982+
for (int i = m_index.tail - 1; i >= 0; i--)
3983+
{
3984+
GSVertex* v = &m_vertex.buff[m_index.buff[i]];
3985+
3986+
// Only Q on the second vertex is valid
3987+
if (!(i & 1) && is_sprite)
3988+
v->RGBAQ.Q = m_vertex.buff[m_index.buff[i + 1]].RGBAQ.Q;
3989+
3990+
int T = std::bit_cast<int>(v->ST.T);
3991+
int Q = std::bit_cast<int>(v->RGBAQ.Q);
3992+
int S = std::bit_cast<int>(v->ST.S);
3993+
const int expS = (S >> 23) & 0xff;
3994+
const int expT = (T >> 23) & 0xff;
3995+
const int expQ = (Q >> 23) & 0xff;
3996+
int max_exp = std::max(expS, expQ);
3997+
3998+
u32 mask = CalcMask(expS, max_exp);
3999+
S &= ~mask;
4000+
v->ST.S = std::bit_cast<float>(S);
4001+
max_exp = std::max(expT, expQ);
4002+
mask = CalcMask(expT, max_exp);
4003+
T &= ~mask;
4004+
v->ST.T = std::bit_cast<float>(T);
4005+
Q &= ~0xff;
4006+
4007+
if (!is_sprite || (i & 1))
4008+
v->RGBAQ.Q = std::bit_cast<float>(Q);
4009+
4010+
float U = (v->ST.S / v->RGBAQ.Q) * (1 << m_context->TEX0.TW);
4011+
float V = (v->ST.T / v->RGBAQ.Q) * (1 << m_context->TEX0.TH);
4012+
4013+
m_vt.m_min.t.x = std::isnan(U) ? m_vt.m_min.t.x : std::min(m_vt.m_min.t.x, U);
4014+
m_vt.m_min.t.y = std::isnan(V) ? m_vt.m_min.t.y : std::min(m_vt.m_min.t.y, V);
4015+
m_vt.m_max.t.x = std::isnan(U) ? m_vt.m_max.t.x : std::min(m_vt.m_max.t.x, U);
4016+
m_vt.m_max.t.y = std::isnan(V) ? m_vt.m_max.t.y : std::min(m_vt.m_max.t.y, V);
4017+
}
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/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)