diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index bf8ebb9288..87b2917c95 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -27,6 +27,7 @@ #include #include #include +#include int GSState::s_n = 0; int GSState::s_last_transfer_draw_n = 0; @@ -503,7 +504,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 << "(" << std::bit_cast(v.ST.S) << ")" << DEL << v.ST.T << "(" << std::bit_cast(v.ST.T) << ")" << DEL << v.RGBAQ.Q << "(" << std::bit_cast(v.RGBAQ.Q) << ")"; file << std::endl; } @@ -1593,6 +1594,13 @@ inline bool GSState::TestDrawChanged() return false; } +u32 GSState::CalcMask(int exp, int max_exp) +{ + const int amount = 9 + (max_exp - exp); + + return (1 << std::min(amount, 23)) - 1; +} + void GSState::FlushPrim() { if (m_index.tail > 0) @@ -1668,6 +1676,50 @@ void GSState::FlushPrim() m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); + // Texel coordinate rounding + // Helps Manhunt (lights shining through objects). + // Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers. + // Sometimes hardware doesn't get affected, likely due to the difference in how GPU's handle textures (Persona minimap). + if (m_env.PRIM.TME && (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS || m_vt.m_eq.z)) + { + if (!m_env.PRIM.FST) // STQ's + { + const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS; + // ST's have the lowest 9 bits (or greater depending on exponent difference) rounding down (from hardware tests). + for (int i = m_index.tail - 1; i >= 0; i--) + { + GSVertex* v = &m_vertex.buff[m_index.buff[i]]; + + // Only Q on the second vertex is valid + if (!(i & 1) && is_sprite) + v->RGBAQ.Q = m_vertex.buff[m_index.buff[i + 1]].RGBAQ.Q; + + int T = std::bit_cast(v->ST.T); + int Q = std::bit_cast(v->RGBAQ.Q); + int S = std::bit_cast(v->ST.S); + const int expS = (S >> 23) & 0xff; + const int expT = (T >> 23) & 0xff; + const int expQ = (Q >> 23) & 0xff; + int max_exp = std::max(expS, expQ); + + u32 mask = CalcMask(expS, max_exp); + S &= ~mask; + v->ST.S = std::bit_cast(S); + max_exp = std::max(expT, expQ); + mask = CalcMask(expT, max_exp); + T &= ~mask; + v->ST.T = std::bit_cast(T); + Q &= ~0xff; + + if (!is_sprite || (i & 1)) + v->RGBAQ.Q = std::bit_cast(Q); + + 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)); + 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)); + } + } + } + // Skip draw if Z test is enabled, but set to fail all pixels. const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER); diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index 4a6f214c55..0e49400269 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -383,6 +383,7 @@ public: 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();