@@ -1677,49 +1677,7 @@ void GSState::FlushPrim()
1677
1677
1678
1678
m_vt.Update (m_vertex.buff , m_index.buff , m_vertex.tail , m_index.tail , GSUtil::GetPrimClass (PRIM->PRIM ));
1679
1679
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 ();
1723
1681
1724
1682
// Skip draw if Z test is enabled, but set to fail all pixels.
1725
1683
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
3831
3789
3832
3790
u8 uses_border = 0 ;
3833
3791
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 )
3836
3794
{
3837
3795
// If any of the min/max values are +-FLT_MAX we can't rely on them
3838
3796
// so just assume full texture.
@@ -4009,6 +3967,64 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
4009
3967
return { vr, uses_border };
4010
3968
}
4011
3969
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
+
4012
4028
void GSState::CalcAlphaMinMax (const int tex_alpha_min, const int tex_alpha_max)
4013
4029
{
4014
4030
if (m_vt.m_alpha .valid && tex_alpha_min == 0 && tex_alpha_max == 255 )
0 commit comments