Skip to content

Commit 078f149

Browse files
committed
GS: Add utility functions for vertices/quads.
1 parent 5cb7abf commit 078f149

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

pcsx2/GS/GSState.cpp

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4540,6 +4540,256 @@ __forceinline bool GSState::EarlyDetectShuffle(u32 prim)
45404540
return false;
45414541
}
45424542

4543+
__fi GSVector4 GSState::GetXYWindow(const GSVertex& v)
4544+
{
4545+
return GSVector4(GetVertexXY(v) - m_context->scissor.xyof.xyxy()) / 16.0f;
4546+
}
4547+
4548+
template<bool fst>
4549+
__fi GSVector4 GSState::GetTexCoordsImpl(const GSVertex& v, float q)
4550+
{
4551+
if constexpr (fst)
4552+
{
4553+
return GSVector4(GetVertexUV(v)) / 16.0f;
4554+
}
4555+
else
4556+
{
4557+
const float tw = static_cast<float>(1 << m_context->TEX0.TW);
4558+
const float th = static_cast<float>(1 << m_context->TEX0.TH);
4559+
const GSVector4 tex_size(tw, th, tw, th);
4560+
return GSVector4(GetVertexST(v) / q * tex_size);
4561+
}
4562+
}
4563+
4564+
template<bool fst>
4565+
__fi GSVector4 GSState::GetTexCoordsImpl(const GSVertex& v)
4566+
{
4567+
return GetTexCoordsImpl<fst>(v, v.RGBAQ.Q);
4568+
}
4569+
4570+
__fi GSVector4 GSState::GetTexCoords(const GSVertex& v, float q)
4571+
{
4572+
if (PRIM->FST)
4573+
{
4574+
return GetTexCoordsImpl<true>(v, q);
4575+
}
4576+
else
4577+
{
4578+
return GetTexCoordsImpl<false>(v, q);
4579+
}
4580+
}
4581+
4582+
__fi GSVector4 GSState::GetTexCoords(const GSVertex& v)
4583+
{
4584+
return GetTexCoords(v, v.RGBAQ.Q);
4585+
}
4586+
4587+
template<u32 primclass, bool tme, bool fst>
4588+
bool GSState::GetQuadCornersImpl(const GSVertex* v, const u16* i, GSVertex& vout0, GSVertex& vout1)
4589+
{
4590+
static_assert(primclass == GS_SPRITE_CLASS || primclass == GS_TRIANGLE_CLASS);
4591+
4592+
if constexpr (primclass == GS_TRIANGLE_CLASS)
4593+
{
4594+
TriangleOrdering tri0;
4595+
TriangleOrdering tri1;
4596+
4597+
const u16* i0 = i + 0;
4598+
const u16* i1 = i + 3;
4599+
4600+
if (!AreTrianglesQuad<tme, fst>(v, i0, i1, &tri0, &tri1))
4601+
return false;
4602+
4603+
vout0 = v[i0[tri0.b]];
4604+
vout1 = v[i1[tri1.b]];
4605+
}
4606+
else
4607+
{
4608+
// primclass == GS_SPRITE_CLASS
4609+
vout0 = v[i[0]];
4610+
vout1 = v[i[1]];
4611+
}
4612+
4613+
return true;
4614+
}
4615+
4616+
template<u32 primclass>
4617+
void GSState::GetQuadBBoxWindowImpl(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout)
4618+
{
4619+
const GSVector4 xy0 = GetXYWindow(v0);
4620+
const GSVector4 xy1 = GetXYWindow(v1);
4621+
4622+
xyout = xy0.min(xy1).xyzw(xy0.max(xy1));
4623+
}
4624+
4625+
template<u32 primclass, bool tme, bool fst>
4626+
void GSState::GetQuadBBoxWindowImpl(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout, GSVector4& texout, bool keep_tex_order)
4627+
{
4628+
if constexpr (!tme)
4629+
{
4630+
GetQuadBBoxWindowImpl<primclass>(v0, v1, xyout);
4631+
return;
4632+
}
4633+
4634+
GSVector4 xy0 = GetXYWindow(v0);
4635+
GSVector4 xy1 = GetXYWindow(v1);
4636+
GSVector4 tex0 = GetTexCoordsImpl<fst>(v0, primclass == GS_SPRITE_CLASS ? v1.RGBAQ.Q : v0.RGBAQ.Q);
4637+
GSVector4 tex1 = GetTexCoordsImpl<fst>(v1, v1.RGBAQ.Q);
4638+
4639+
if (!keep_tex_order)
4640+
{
4641+
xyout = xy0.min(xy1).xyzw(xy0.max(xy1));
4642+
texout = tex0.min(tex1).xyzw(tex0.max(tex1));
4643+
}
4644+
else
4645+
{
4646+
xyout = xy0.xyzw(xy1);
4647+
texout = tex0.xyzw(tex1);
4648+
4649+
const int swap = (xy0 > xy1).mask();
4650+
4651+
if (swap & 1)
4652+
{
4653+
xyout = xyout.zyxw();
4654+
texout = texout.zyxw();
4655+
}
4656+
4657+
if (swap & 2)
4658+
{
4659+
xyout = xyout.xwzy();
4660+
texout = texout.xwzy();
4661+
}
4662+
}
4663+
}
4664+
4665+
#define GEN_TMPL_SELECT_1(func, ...) \
4666+
if (m_vt.m_primclass == GS_TRIANGLE_CLASS) \
4667+
{ \
4668+
func<GS_TRIANGLE_CLASS>(__VA_ARGS__); \
4669+
} \
4670+
else if (m_vt.m_primclass == GS_SPRITE_CLASS) \
4671+
{ \
4672+
func<GS_SPRITE_CLASS>(__VA_ARGS__); \
4673+
} \
4674+
else \
4675+
{ \
4676+
pxFail("Wrong prim class."); \
4677+
}
4678+
4679+
4680+
#define GEN_TMPL_SELECT_2(func, ...) \
4681+
if (m_vt.m_primclass == GS_TRIANGLE_CLASS) \
4682+
{ \
4683+
if (PRIM->TME) \
4684+
{ \
4685+
if (PRIM->FST) \
4686+
{ \
4687+
return func<GS_TRIANGLE_CLASS, true, true>(__VA_ARGS__); \
4688+
} \
4689+
else \
4690+
{ \
4691+
return func<GS_TRIANGLE_CLASS, true, false>(__VA_ARGS__); \
4692+
} \
4693+
} \
4694+
else \
4695+
{ \
4696+
return func<GS_TRIANGLE_CLASS>(__VA_ARGS__); \
4697+
} \
4698+
} \
4699+
else if (m_vt.m_primclass == GS_SPRITE_CLASS) \
4700+
{ \
4701+
if (PRIM->TME) \
4702+
{ \
4703+
if (PRIM->FST) \
4704+
{ \
4705+
return func<GS_SPRITE_CLASS, true, true>(__VA_ARGS__); \
4706+
} \
4707+
else \
4708+
{ \
4709+
return func<GS_SPRITE_CLASS, true, false>(__VA_ARGS__); \
4710+
} \
4711+
} \
4712+
else \
4713+
{ \
4714+
return func<GS_SPRITE_CLASS>(__VA_ARGS__); \
4715+
} \
4716+
} \
4717+
else \
4718+
{ \
4719+
pxFail("Wrong prim class."); \
4720+
}
4721+
4722+
bool GSState::GetQuadCorners(const GSVertex* v, const u16* i, GSVertex& vout0, GSVertex& vout1)
4723+
{
4724+
GEN_TMPL_SELECT_2(GetQuadCornersImpl, v, i, vout0, vout1);
4725+
return false;
4726+
}
4727+
4728+
void GSState::GetQuadBBoxWindow(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout)
4729+
{
4730+
GEN_TMPL_SELECT_1(GetQuadBBoxWindowImpl, v0, v1, xyout);
4731+
}
4732+
4733+
void GSState::GetQuadBBoxWindow(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout, GSVector4& texout, bool keep_tex_order)
4734+
{
4735+
GEN_TMPL_SELECT_2(GetQuadBBoxWindowImpl, v0, v1, xyout, texout, keep_tex_order);
4736+
}
4737+
4738+
#undef GEN_TMPL_SELECT_2
4739+
#undef GEN_TMPL_SELECT_1
4740+
4741+
void GSState::GetQuadRasterizedPoints(GSVector4& xy, GSVector4& tex, bool keep_order)
4742+
{
4743+
// Swap so that coordinates are top-left and bottom-right.
4744+
const int swap = (xy.xyxy() > xy.zwzw()).mask();
4745+
4746+
if (swap & 1)
4747+
{
4748+
xy = xy.zyxw();
4749+
tex = tex.zyxw();
4750+
}
4751+
4752+
if (swap & 2)
4753+
{
4754+
xy = xy.xwzy();
4755+
tex = tex.xwzy();
4756+
}
4757+
4758+
const GSVector4 grad = (tex.zwzw() - tex.xyxy()) / (xy.zwzw() - xy.xyxy());
4759+
4760+
// Round XY to contained pixels. Omit bottom-right pixels on the edge.
4761+
GSVector4 xy_round = xy.ceil().xyzw(xy.floor());
4762+
const GSVector4 bottom_right = GSVector4::zero().xyzw(xy == xy_round);
4763+
xy_round = xy_round.blend32(xy_round - GSVector4(1.0f), bottom_right);
4764+
4765+
// Interpolate texture coords.
4766+
tex += grad * (xy_round - xy);
4767+
4768+
xy = xy_round;
4769+
4770+
// Swap back to original order if needed.
4771+
if (keep_order)
4772+
{
4773+
if (swap & 1)
4774+
{
4775+
xy = xy.zyxw();
4776+
tex = tex.zyxw();
4777+
}
4778+
4779+
if (swap & 2)
4780+
{
4781+
xy = xy.xwzy();
4782+
tex = tex.xwzy();
4783+
}
4784+
}
4785+
}
4786+
4787+
void GSState::GetQuadRasterizedPoints(GSVector4& xy, bool keep_order)
4788+
{
4789+
GSVector4 tex_ignore;
4790+
GetQuadRasterizedPoints(xy, tex_ignore, keep_order);
4791+
}
4792+
45434793
__forceinline bool GSState::IsAutoFlushDraw(u32 prim, int& tex_layer)
45444794
{
45454795
if (!PRIM->TME || (GSConfig.UserHacks_AutoFlush == GSHWAutoFlushLevel::SpritesOnly && prim != GS_SPRITE))

pcsx2/GS/GSState.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,32 @@ class GSState : public GSAlignedClass<32>
210210
void CalcAlphaMinMax(const int tex_min, const int tex_max);
211211
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
212212

213+
// Utility functions for getting position/texture coordinates.
214+
GSVector4 GetXYWindow(const GSVertex& v);
215+
template<bool fst>
216+
GSVector4 GetTexCoordsImpl(const GSVertex& v, float q);
217+
template<bool fst>
218+
GSVector4 GetTexCoordsImpl(const GSVertex& v);
219+
GSVector4 GetTexCoords(const GSVertex& v, float q);
220+
GSVector4 GetTexCoords(const GSVertex& v);
221+
222+
// Utility functions to detect and get corners of quads.
223+
template<u32 primclass, bool tme = false, bool fst = false>
224+
static bool GetQuadCornersImpl(const GSVertex* v, const u16* i, GSVertex& vout0, GSVertex& vout1);
225+
bool GetQuadCorners(const GSVertex* v, const u16* i, GSVertex& vout0, GSVertex& vout1);
226+
227+
// Utility functions to get window/texture coordinates of a quad.
228+
template<u32 primclass>
229+
void GetQuadBBoxWindowImpl(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout);
230+
template<u32 primclass, bool tme = false, bool fst = false>
231+
void GetQuadBBoxWindowImpl(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout, GSVector4& texout, bool keep_tex_order = true);
232+
void GetQuadBBoxWindow(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout);
233+
void GetQuadBBoxWindow(const GSVertex& v0, const GSVertex& v1, GSVector4& xyout, GSVector4& texout, bool keep_tex_order = true);
234+
235+
// Adjusts a quad so that it contains exactly the centers of the pixels that the GS would rasterize.
236+
static void GetQuadRasterizedPoints(GSVector4& xy, bool keep_order = true);
237+
static void GetQuadRasterizedPoints(GSVector4& xy, GSVector4& tex, bool keep_order = true);
238+
213239
public:
214240
enum EEGS_TransferType
215241
{

pcsx2/GS/Renderers/Common/GSVertex.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,37 @@ struct alignas(32) GSVertexPT1
4444

4545
static_assert(sizeof(GSVertexPT1) == sizeof(GSVertex));
4646

47+
__forceinline_odr GSVector4i GetVertexXY(const GSVertex& v)
48+
{
49+
return GSVector4i(v.m[1]).upl16().xyxy();
50+
}
51+
52+
__forceinline_odr GSVector4i GetVertexZ(const GSVertex& v)
53+
{
54+
return GSVector4i(v.m[1]).yyyy();
55+
}
56+
57+
__forceinline_odr GSVector4i GetVertexUV(const GSVertex& v)
58+
{
59+
return GSVector4i(v.m[1]).uph16().xyxy();
60+
}
61+
62+
__forceinline_odr GSVector4 GetVertexST(const GSVertex& v)
63+
{
64+
return GSVector4::cast(GSVector4i(v.m[0])).xyxy();
65+
}
66+
67+
__forceinline_odr GSVector4i GetVertexRGBA(const GSVertex& v)
68+
{
69+
return GSVector4i(v.m[0]).uph8().upl16();
70+
}
71+
72+
__forceinline_odr GSVector4 GetVertexQ(const GSVertex& v)
73+
{
74+
return GSVector4::cast(GSVector4i(v.m[0])).wwww();
75+
}
76+
77+
__forceinline_odr GSVector4i GetVertexFOG(const GSVertex& v)
78+
{
79+
return GSVector4i(v.m[1]).wwww();
80+
}

0 commit comments

Comments
 (0)