Skip to content

Commit e435989

Browse files
committed
GS: Instead of culling primitive, replace NaN ST with 0.
1 parent 1f97d5a commit e435989

File tree

2 files changed

+72
-131
lines changed

2 files changed

+72
-131
lines changed

pcsx2/GS/GSState.cpp

Lines changed: 70 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -4156,8 +4156,6 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
41564156
// Sometimes hardware doesn't get affected, likely due to the difference in how GPU's handle textures (Persona minimap).
41574157
void GSState::RoundSTCoords()
41584158
{
4159-
const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS;
4160-
41614159
// ST's have the lowest 9 bits (or greater depending on exponent difference) rounded down (from hardware tests).
41624160
// This gives the bitmask for the lower 9 (or more) bits.
41634161
auto LowerBitsMask = [](int exp, int max_exp)
@@ -4170,9 +4168,11 @@ void GSState::RoundSTCoords()
41704168
{
41714169
GSVertex* v = &m_vertex.buff[m_index.buff[i]];
41724170

4173-
// Only Q on the second vertex is valid
4174-
if (!(i & 1) && is_sprite)
4175-
v->RGBAQ.Q = m_vertex.buff[m_index.buff[i + 1]].RGBAQ.Q;
4171+
if (m_vt.m_primclass == GS_SPRITE_CLASS && (i & 1))
4172+
{
4173+
// FIXME: Remove this once done debugging
4174+
pxAssertMsg(m_vertex.buff[m_index.buff[i]].RGBAQ.Q == m_vertex.buff[m_index.buff[i - 1]].RGBAQ.Q, "Sprite Qs different");
4175+
}
41764176

41774177
int S = std::bit_cast<int>(v->ST.S);
41784178
int T = std::bit_cast<int>(v->ST.T);
@@ -4189,9 +4189,6 @@ void GSState::RoundSTCoords()
41894189
v->ST.S = std::bit_cast<float>(S);
41904190
v->ST.T = std::bit_cast<float>(T);
41914191

4192-
if (!is_sprite || (i & 1))
4193-
v->RGBAQ.Q = std::bit_cast<float>(Q);
4194-
41954192
const float U = (v->ST.S / v->RGBAQ.Q) * (1 << m_context->TEX0.TW);
41964193
const float V = (v->ST.T / v->RGBAQ.Q) * (1 << m_context->TEX0.TH);
41974194
const float Qf = std::bit_cast<float>(Q);
@@ -4208,48 +4205,39 @@ void GSState::RoundSTCoords()
42084205
m_vt.m_max.t = m_vt.m_max.t.min(GSVector4(2047.0f)).max(GSVector4(-2047.0f)).xyzw(m_vt.m_max.t);
42094206
}
42104207

4211-
// Handle the huge ST coords in by culling primitives with NaN coords and
4212-
// replacing the primitives with huge coords with a new one that has the huge coordinate replaced with +/- 2047.
4208+
// Handle the huge or NaN ST coords culling primitives or replacing
4209+
// replacing the primitives with valid coordinates.
42134210
// This is based on hardware test that show that seem to show that ST coordinate get clamped to +/- 2047
4214-
// (perhaps before applying repeat or region repeat).
4215-
// Note that the huge texture coords may be a symptom of floating point issues upstream in the EE and
4216-
// it would be better to have them fixed there; this is a bandaid.
4211+
// before applying repeat or region repeat.
4212+
// Note that the huge texture coords may be a symptom of floating point issues in the EE and
4213+
// it would be better to have them fixed there.
42174214
void GSState::FixHugeSTCoords()
42184215
{
4219-
bool sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_SPRITE_CLASS;
42204216
switch (GSUtil::GetClassVertexCount(GSUtil::GetPrimClass(PRIM->PRIM)))
42214217
{
42224218
case 1:
4223-
if (sprite)
4224-
FixHugeSTCoordsImpl<1, true>();
4225-
else
4226-
FixHugeSTCoordsImpl<1, false>();
4219+
FixHugeSTCoordsImpl<1, false>();
42274220
break;
42284221
case 2:
4229-
if (sprite)
4230-
FixHugeSTCoordsImpl<2, true>();
4231-
else
4232-
FixHugeSTCoordsImpl<2, false>();
4222+
FixHugeSTCoordsImpl<2, false>();
42334223
break;
42344224
case 3:
4235-
if (sprite)
4236-
FixHugeSTCoordsImpl<3, true>();
4237-
else
4238-
FixHugeSTCoordsImpl<3, false>();
4225+
FixHugeSTCoordsImpl<3, false>();
42394226
break;
42404227
default:
42414228
pxFail("Impossible");
42424229
}
42434230
}
42444231

4245-
template <u32 n, bool sprite> void GSState::FixHugeSTCoordsImpl()
4232+
template <u32 n, bool cull>
4233+
void GSState::FixHugeSTCoordsImpl()
42464234
{
42474235
GSVertex* const vertex = m_vertex.buff;
42484236
u16* const index = m_index.buff;
42494237

42504238
u32 new_index_tail = 0;
42514239

4252-
constexpr float huge = 1e10f; // arbitrary large value
4240+
constexpr float huge = 1e10f; // Arbitrary large value
42534241

42544242
const float tex_width = 1 << m_context->TEX0.TW;
42554243
const float tex_height = 1 << m_context->TEX0.TH;
@@ -4260,138 +4248,92 @@ template <u32 n, bool sprite> void GSState::FixHugeSTCoordsImpl()
42604248
{
42614249
bool nan_s = false;
42624250
bool nan_t = false;
4263-
bool huge_s_pos = false;
4264-
bool huge_s_neg = false;
4265-
bool huge_t_pos = false;
4266-
bool huge_t_neg = false;
4251+
bool huge_pos_s = false;
4252+
bool huge_neg_s = false;
4253+
bool huge_pos_t = false;
4254+
bool huge_neg_t = false;
42674255

4268-
if (sprite)
4256+
if (m_vt.m_primclass == GS_SPRITE_CLASS)
42694257
{
4270-
// Sprites behave as if both Qs are same as the second one
4271-
const float s0 = vertex[index[i + 0]].ST.S / vertex[index[i + 1]].RGBAQ.Q;
4272-
const float t0 = vertex[index[i + 0]].ST.T / vertex[index[i + 1]].RGBAQ.Q;
4273-
const float s1 = vertex[index[i + 1]].ST.S / vertex[index[i + 1]].RGBAQ.Q;
4274-
const float t1 = vertex[index[i + 1]].ST.T / vertex[index[i + 1]].RGBAQ.Q;
4275-
nan_s = std::isnan(s0) || std::isnan(s1);
4276-
nan_t = std::isnan(t0) || std::isnan(t1);
4277-
huge_s_pos = s0 > huge || s1 > huge;
4278-
huge_s_neg = s0 < -huge || s1 < -huge;
4279-
huge_t_pos = t0 > huge || t1 > huge;
4280-
huge_t_neg = t0 < -huge || t1 < -huge;
4258+
// FIXME: Remove this once done debugging
4259+
pxAssertMsg(vertex[index[i + 0]].RGBAQ.Q == vertex[index[i + 1]].RGBAQ.Q, "Sprite Qs different");
42814260
}
4282-
else
4261+
4262+
for (u32 j = 0; j < n; j++)
42834263
{
4284-
for (u32 j = 0; j < n; j++)
4285-
{
4286-
const float s = vertex[index[i + j]].ST.S / vertex[index[i + j]].RGBAQ.Q;
4287-
const float t = vertex[index[i + j]].ST.T / vertex[index[i + j]].RGBAQ.Q;
4288-
nan_s |= std::isnan(s);
4289-
nan_t |= std::isnan(t);
4290-
huge_s_pos |= s > huge;
4291-
huge_t_pos |= t > huge;
4292-
huge_s_neg |= s < -huge;
4293-
huge_t_neg |= t < -huge;
4294-
}
4264+
const float s = vertex[index[i + j]].ST.S / vertex[index[i + j]].RGBAQ.Q;
4265+
const float t = vertex[index[i + j]].ST.T / vertex[index[i + j]].RGBAQ.Q;
4266+
nan_s |= std::isnan(s);
4267+
nan_t |= std::isnan(t);
4268+
huge_pos_s |= s > huge;
4269+
huge_pos_t |= t > huge;
4270+
huge_neg_s |= s < -huge;
4271+
huge_neg_t |= t < -huge;
42954272
}
42964273

42974274
// ambiguous = true would probably result in NaN in the SW rasterizer or something undefined in HW.
42984275
// PS2 does not have NaN so there is no really accurate way to emulate this.
42994276
// huge = true and ambiguous = false seems to have well-defined behavior on the PS2:
43004277
// it clamps huge values to +/-2047 in UV coordinates space. We try to approximate this by
43014278
// giving ST the values that would result in exactly +/-2047 across the primitive.
4302-
const bool ambiguous = nan_s || nan_t || (huge_s_pos && huge_s_neg) || (huge_s_pos && huge_s_neg);
4303-
const bool huge = huge_s_pos || huge_t_pos || huge_s_neg || huge_t_neg;
4279+
// For ambiguous values either cull the primitive or replace coordinates by 0.
4280+
const bool ambiguous_s = nan_s || (huge_pos_s && huge_neg_s);
4281+
const bool ambiguous_t = nan_t || (huge_pos_t && huge_neg_t);
43044282

4305-
if (ambiguous)
4283+
if ((ambiguous_s || ambiguous_t) && cull)
43064284
{
43074285
// Cull the primitive by not saving the indices
43084286
continue;
43094287
}
43104288

4311-
if (huge)
4289+
if (huge_pos_s || huge_pos_t || huge_neg_s || huge_neg_t || ambiguous_s || ambiguous_t)
43124290
{
43134291
// Add new vertices to replace the primitive with another primitive with clamped values.
43144292
new_prims = true;
4315-
4316-
if (sprite)
4293+
4294+
// Copy old values to tail of vertex buffer.
4295+
// The vertex buffer is allocated so that there is always at least room for 3 new vertices at the end.
4296+
for (u32 j = 0; j < n; j++)
4297+
vertex[m_vertex.tail + j] = vertex[index[i + j]];
4298+
4299+
const float new_u_val = ambiguous_s ? 0.0f :
4300+
huge_pos_s ? 2047.0f :
4301+
huge_neg_s ? -2047.0f :
4302+
NAN;
4303+
const float new_v_val = ambiguous_t ? 0.0f :
4304+
huge_pos_t ? 2047.0f :
4305+
huge_neg_t ? -2047.0f :
4306+
NAN;
4307+
4308+
// If we are replacing both S and T, replace Q by 1.0f
4309+
if (!std::isnan(new_u_val) && !std::isnan(new_v_val))
43174310
{
4318-
// Handle sprite separately since it uses the second Q for both vertices
4319-
GSVertex v_new0 = vertex[index[i + 0]];
4320-
GSVertex v_new1 = vertex[index[i + 1]];
4321-
4322-
// Try to set values so that we get constant UV +/-2047 across the entire triangle after interpolation
4323-
// Sprites behave as if both Qs are same as the second one
4324-
if (huge_s_pos)
4325-
{
4326-
v_new1.ST.S = v_new0.ST.S = 2047.0f * v_new1.RGBAQ.Q / tex_width;
4327-
}
4328-
else if (huge_s_neg)
4329-
{
4330-
v_new1.ST.S = v_new0.ST.S = -2047.0f * v_new1.RGBAQ.Q / tex_width;
4331-
}
4332-
4333-
if (huge_t_pos)
4334-
{
4335-
v_new1.ST.T = v_new0.ST.T = 2047.0f * v_new1.RGBAQ.Q / tex_height;
4336-
}
4337-
else if (huge_t_neg)
4338-
{
4339-
v_new1.ST.T = v_new0.ST.T = -2047.0f * v_new1.RGBAQ.Q / tex_height;
4340-
}
4341-
4342-
// Copy old values to tail of vertex buffer.
4343-
// The vertex buffer is allocated so that there is always at least room for 3 new vertices at the end.
4344-
vertex[m_vertex.tail + 0] = v_new0;
4345-
vertex[m_vertex.tail + 1] = v_new1;
4346-
4347-
// Make new indices point to new vertices
4348-
index[new_index_tail + 0] = m_vertex.tail + 0;
4349-
index[new_index_tail + 1] = m_vertex.tail + 1;
4311+
for (u32 j = 0; j < n; j++)
4312+
vertex[m_vertex.tail + j].RGBAQ.Q = 1.0f;
43504313
}
4351-
else
4314+
4315+
// Try to replace huge/ambiguous values so that we get constant U or V across the entire primitive after interpolation
4316+
if (!std::isnan(new_u_val))
43524317
{
4353-
// Copy old values to tail of vertex buffer.
4354-
// The vertex buffer is allocated so that there is always at least room for 3 new vertices at the end.
43554318
for (u32 j = 0; j < n; j++)
4356-
vertex[m_vertex.tail + j] = vertex[index[i + j]];
4357-
4358-
// Try to set values so that we get constant UV +/-2047 across the entire primitive after interpolation
4359-
if (huge_s_pos)
4360-
{
4361-
for (u32 j = 0; j < n; j++)
4362-
vertex[m_vertex.tail + j].ST.S = 2047.0f * vertex[m_vertex.tail + j].RGBAQ.Q / tex_width;
4363-
}
4364-
else if (huge_s_neg)
4365-
{
4366-
for (u32 j = 0; j < n; j++)
4367-
vertex[m_vertex.tail + j].ST.S = -2047.0f * vertex[m_vertex.tail + j].RGBAQ.Q / tex_width;
4368-
}
4369-
4370-
if (huge_t_pos)
4371-
{
4372-
for (int j = 0; j < n; j++)
4373-
vertex[m_vertex.tail + j].ST.T = 2047.0f * vertex[m_vertex.tail + j].RGBAQ.Q / tex_height;
4374-
}
4375-
else if (huge_t_neg)
4376-
{
4377-
for (u32 j = 0; j < n; j++)
4378-
vertex[m_vertex.tail + j].ST.T = -2047.0f * vertex[m_vertex.tail + j].RGBAQ.Q / tex_height;
4379-
}
4319+
vertex[m_vertex.tail + j].ST.S = new_u_val * vertex[m_vertex.tail + j].RGBAQ.Q / tex_width;
4320+
}
43804321

4381-
// Make new indices point to new vertices
4322+
if (!std::isnan(new_v_val))
4323+
{
43824324
for (u32 j = 0; j < n; j++)
4383-
{
4384-
index[new_index_tail + j] = m_vertex.tail + j;
4385-
}
4325+
vertex[m_vertex.tail + j].ST.T = new_v_val * vertex[m_vertex.tail + j].RGBAQ.Q / tex_width;
43864326
}
43874327

4328+
// Make new indices point to new vertices
4329+
for (u32 j = 0; j < n; j++)
4330+
index[new_index_tail + j] = m_vertex.tail + j;
4331+
43884332
// Advance tail since we pushed new vertices
43894333
m_vertex.tail += n;
43904334

43914335
if (m_vertex.tail >= m_vertex.maxcount)
4392-
{
43934336
GrowVertexBuffer();
4394-
}
43954337
}
43964338
else if (new_index_tail < i) // If new_index_tail == i, don't update indices since no primitives have been culled
43974339
{
@@ -4405,11 +4347,9 @@ template <u32 n, bool sprite> void GSState::FixHugeSTCoordsImpl()
44054347

44064348
m_index.tail = new_index_tail;
44074349

4350+
// If indexed new primitives at the end of the buffer, update head and next also
44084351
if (new_prims)
4409-
{
4410-
// We indexed new primitives at the end of the buffer so update head and next also
44114352
m_vertex.head = m_vertex.next = m_vertex.tail;
4412-
}
44134353
}
44144354

44154355
void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)

pcsx2/GS/GSState.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ class GSState : public GSAlignedClass<32>
207207
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
208208
void RoundSTCoords();
209209
void FixHugeSTCoords();
210-
template <u32 n, bool sprite> void FixHugeSTCoordsImpl();
210+
template <u32 n, bool cull>
211+
void FixHugeSTCoordsImpl();
211212

212213
public:
213214
struct GSUploadQueue

0 commit comments

Comments
 (0)