GS: Remove all usage of provoking first/flat swapped in early pipeline.

Removes said usage from GSState and GSVertexTrace (and helper classes). The end goal is to support first-vertex-provoking APIs in GSRendererHW instead of early in the pipeline.
This commit is contained in:
TJnotJT 2025-07-06 23:20:09 -04:00
parent 099e1f900d
commit ed2e3a10bf
5 changed files with 80 additions and 105 deletions

View File

@ -27,11 +27,6 @@ static __fi bool IsAutoFlushEnabled()
return GSIsHardwareRenderer() ? (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled) : GSConfig.AutoFlushSW;
}
static __fi bool IsFirstProvokingVertex()
{
return (GSIsHardwareRenderer() && !g_gs_device->Features().provoking_vertex_last);
}
constexpr int GSState::GetSaveStateSize(int version)
{
int size = 0;
@ -99,7 +94,7 @@ constexpr int GSState::GetSaveStateSize(int version)
}
GSState::GSState()
: m_vt(this, IsFirstProvokingVertex())
: m_vt(this)
{
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
// Let's keep it disabled to ease debug.
@ -206,29 +201,29 @@ void GSState::Reset(bool hardware_reset)
memcpy(&m_prev_env, &m_env, sizeof(m_prev_env));
}
template<bool auto_flush, bool index_swap>
template<bool auto_flush>
void GSState::SetPrimHandlers()
{
#define SetHandlerXYZ(P, auto_flush, index_swap) \
m_fpGIFPackedRegHandlerXYZ[P][0] = &GSState::GIFPackedRegHandlerXYZF2<P, 0, auto_flush, index_swap>; \
m_fpGIFPackedRegHandlerXYZ[P][1] = &GSState::GIFPackedRegHandlerXYZF2<P, 1, auto_flush, index_swap>; \
m_fpGIFPackedRegHandlerXYZ[P][2] = &GSState::GIFPackedRegHandlerXYZ2<P, 0, auto_flush, index_swap>; \
m_fpGIFPackedRegHandlerXYZ[P][3] = &GSState::GIFPackedRegHandlerXYZ2<P, 1, auto_flush, index_swap>; \
m_fpGIFRegHandlerXYZ[P][0] = &GSState::GIFRegHandlerXYZF2<P, 0, auto_flush, index_swap>; \
m_fpGIFRegHandlerXYZ[P][1] = &GSState::GIFRegHandlerXYZF2<P, 1, auto_flush, index_swap>; \
m_fpGIFRegHandlerXYZ[P][2] = &GSState::GIFRegHandlerXYZ2<P, 0, auto_flush, index_swap>; \
m_fpGIFRegHandlerXYZ[P][3] = &GSState::GIFRegHandlerXYZ2<P, 1, auto_flush, index_swap>; \
m_fpGIFPackedRegHandlerSTQRGBAXYZF2[P] = &GSState::GIFPackedRegHandlerSTQRGBAXYZF2<P, auto_flush, index_swap>; \
m_fpGIFPackedRegHandlerSTQRGBAXYZ2[P] = &GSState::GIFPackedRegHandlerSTQRGBAXYZ2<P, auto_flush, index_swap>;
#define SetHandlerXYZ(P, auto_flush) \
m_fpGIFPackedRegHandlerXYZ[P][0] = &GSState::GIFPackedRegHandlerXYZF2<P, 0, auto_flush>; \
m_fpGIFPackedRegHandlerXYZ[P][1] = &GSState::GIFPackedRegHandlerXYZF2<P, 1, auto_flush>; \
m_fpGIFPackedRegHandlerXYZ[P][2] = &GSState::GIFPackedRegHandlerXYZ2<P, 0, auto_flush>; \
m_fpGIFPackedRegHandlerXYZ[P][3] = &GSState::GIFPackedRegHandlerXYZ2<P, 1, auto_flush>; \
m_fpGIFRegHandlerXYZ[P][0] = &GSState::GIFRegHandlerXYZF2<P, 0, auto_flush>; \
m_fpGIFRegHandlerXYZ[P][1] = &GSState::GIFRegHandlerXYZF2<P, 1, auto_flush>; \
m_fpGIFRegHandlerXYZ[P][2] = &GSState::GIFRegHandlerXYZ2<P, 0, auto_flush>; \
m_fpGIFRegHandlerXYZ[P][3] = &GSState::GIFRegHandlerXYZ2<P, 1, auto_flush>; \
m_fpGIFPackedRegHandlerSTQRGBAXYZF2[P] = &GSState::GIFPackedRegHandlerSTQRGBAXYZF2<P, auto_flush>; \
m_fpGIFPackedRegHandlerSTQRGBAXYZ2[P] = &GSState::GIFPackedRegHandlerSTQRGBAXYZ2<P, auto_flush>;
SetHandlerXYZ(GS_POINTLIST, true, false);
SetHandlerXYZ(GS_LINELIST, auto_flush, index_swap);
SetHandlerXYZ(GS_LINESTRIP, auto_flush, index_swap);
SetHandlerXYZ(GS_TRIANGLELIST, auto_flush, index_swap);
SetHandlerXYZ(GS_TRIANGLESTRIP, auto_flush, index_swap);
SetHandlerXYZ(GS_TRIANGLEFAN, auto_flush, index_swap);
SetHandlerXYZ(GS_SPRITE, auto_flush, false);
SetHandlerXYZ(GS_INVALID, auto_flush, false);
SetHandlerXYZ(GS_POINTLIST, true);
SetHandlerXYZ(GS_LINELIST, auto_flush);
SetHandlerXYZ(GS_LINESTRIP, auto_flush);
SetHandlerXYZ(GS_TRIANGLELIST, auto_flush);
SetHandlerXYZ(GS_TRIANGLESTRIP, auto_flush);
SetHandlerXYZ(GS_TRIANGLEFAN, auto_flush);
SetHandlerXYZ(GS_SPRITE, auto_flush);
SetHandlerXYZ(GS_INVALID, auto_flush);
#undef SetHandlerXYZ
}
@ -249,11 +244,10 @@ void GSState::ResetHandlers()
m_fpGIFPackedRegHandlers[GIF_REG_A_D] = &GSState::GIFPackedRegHandlerA_D;
m_fpGIFPackedRegHandlers[GIF_REG_NOP] = &GSState::GIFPackedRegHandlerNOP;
// swap first/last indices when the provoking vertex is the first (D3D/Vulkan)
if (IsAutoFlushEnabled())
IsFirstProvokingVertex() ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
SetPrimHandlers<true>();
else
IsFirstProvokingVertex() ? SetPrimHandlers<false, true>() : SetPrimHandlers<false, false>();
SetPrimHandlers<false>();
std::fill(std::begin(m_fpGIFRegHandlers), std::end(m_fpGIFRegHandlers), &GSState::GIFRegHandlerNull);
@ -606,7 +600,7 @@ void GSState::GIFPackedRegHandlerUV_Hack(const GIFPackedReg* RESTRICT r)
m_isPackedUV_HackFlag = true;
}
template <u32 prim, u32 adc, bool auto_flush, bool index_swap>
template <u32 prim, u32 adc, bool auto_flush>
void GSState::GIFPackedRegHandlerXYZF2(const GIFPackedReg* RESTRICT r)
{
const bool skip = adc || r->XYZF2.Skip();
@ -622,10 +616,10 @@ void GSState::GIFPackedRegHandlerXYZF2(const GIFPackedReg* RESTRICT r)
m_v.m[1] = xy.upl32(zf);
VertexKick<prim, auto_flush, index_swap>(skip);
VertexKick<prim, auto_flush>(skip);
}
template <u32 prim, u32 adc, bool auto_flush, bool index_swap>
template <u32 prim, u32 adc, bool auto_flush>
void GSState::GIFPackedRegHandlerXYZ2(const GIFPackedReg* RESTRICT r)
{
const bool skip = adc || r->XYZ2.Skip();
@ -639,7 +633,7 @@ void GSState::GIFPackedRegHandlerXYZ2(const GIFPackedReg* RESTRICT r)
m_v.m[1] = xyz.upl64(GSVector4i::loadl(&m_v.UV));
VertexKick<prim, auto_flush, index_swap>(skip);
VertexKick<prim, auto_flush>(skip);
}
void GSState::GIFPackedRegHandlerFOG(const GIFPackedReg* RESTRICT r)
@ -656,7 +650,7 @@ void GSState::GIFPackedRegHandlerNOP(const GIFPackedReg* RESTRICT r)
{
}
template <u32 prim, bool auto_flush, bool index_swap>
template <u32 prim, bool auto_flush>
void GSState::GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size)
{
pxAssert(size > 0 && size % 3 == 0);
@ -682,7 +676,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u3
m_v.m[1] = xy.upl32(zf); // TODO: only store the last one
VertexKick<prim, auto_flush, index_swap>(r[2].XYZF2.Skip());
VertexKick<prim, auto_flush>(r[2].XYZF2.Skip());
r += 3;
}
@ -690,7 +684,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u3
m_q = r[-3].STQ.Q; // remember the last one, STQ outputs this to the temp Q each time
}
template <u32 prim, bool auto_flush, bool index_swap>
template <u32 prim, bool auto_flush>
void GSState::GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size)
{
pxAssert(size > 0 && size % 3 == 0);
@ -715,7 +709,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32
m_v.m[1] = xyz.upl64(GSVector4i::loadl(&m_v.UV)); // TODO: only store the last one
VertexKick<prim, auto_flush, index_swap>(r[2].XYZ2.Skip());
VertexKick<prim, auto_flush>(r[2].XYZ2.Skip());
r += 3;
}
@ -749,7 +743,7 @@ __forceinline void GSState::ApplyPRIM(u32 prim)
UpdateVertexKick();
pxAssert(m_index.tail == 0 || !g_gs_device->Features().provoking_vertex_last || m_index.buff[m_index.tail - 1] + 1 == m_vertex.next);
pxAssert(m_index.tail == 0 || m_index.buff[m_index.tail - 1] + 1 == m_vertex.next);
if (m_index.tail == 0)
m_vertex.next = 0;
@ -800,7 +794,7 @@ void GSState::GIFRegHandlerUV_Hack(const GIFReg* RESTRICT r)
m_isPackedUV_HackFlag = false;
}
template <u32 prim, u32 adc, bool auto_flush, bool index_swap>
template <u32 prim, u32 adc, bool auto_flush>
void GSState::GIFRegHandlerXYZF2(const GIFReg* RESTRICT r)
{
if (!adc || GSUtil::GetPrimClass(m_prev_env.PRIM.PRIM) != GSUtil::GetPrimClass(m_env.PRIM.PRIM) || (m_dirty_gs_regs & (1 << DIRTY_REG_XYOFFSET)))
@ -812,10 +806,10 @@ void GSState::GIFRegHandlerXYZF2(const GIFReg* RESTRICT r)
m_v.m[1] = xyz.upl64(uvf);
VertexKick<prim, auto_flush, index_swap>(adc);
VertexKick<prim, auto_flush>(adc);
}
template <u32 prim, u32 adc, bool auto_flush, bool index_swap>
template <u32 prim, u32 adc, bool auto_flush>
void GSState::GIFRegHandlerXYZ2(const GIFReg* RESTRICT r)
{
if (!adc || GSUtil::GetPrimClass(m_prev_env.PRIM.PRIM) != GSUtil::GetPrimClass(m_env.PRIM.PRIM) || (m_dirty_gs_regs & (1 << DIRTY_REG_XYOFFSET)))
@ -823,7 +817,7 @@ void GSState::GIFRegHandlerXYZ2(const GIFReg* RESTRICT r)
m_v.m[1] = GSVector4i::load(&r->XYZ, &m_v.UV);
VertexKick<prim, auto_flush, index_swap>(adc);
VertexKick<prim, auto_flush>(adc);
}
template <int i>
@ -3381,7 +3375,7 @@ __forceinline void GSState::CheckCLUTValidity(u32 prim)
}
}
template<u32 prim, bool index_swap>
template<u32 prim>
__forceinline void GSState::HandleAutoFlush()
{
// Kind of a cheat, making the assumption that 2 consecutive fan/strip triangles won't overlap each other (*should* be safe)
@ -3477,9 +3471,8 @@ __forceinline void GSState::HandleAutoFlush()
if (tex_rect.y == tex_rect.w)
tex_rect += GSVector4i::cxpr(0, 0, 0, 1);
const bool swap_index = index_swap && GSUtil::GetPrimClass(m_prev_env.PRIM.PRIM) != GS_SPRITE_CLASS;
// Get the last texture position from the last draw.
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - (swap_index ? n : 1)]];
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - 1]];
if (PRIM->FST)
{
@ -3657,7 +3650,7 @@ __forceinline void GSState::HandleAutoFlush()
}
}
template <u32 prim, bool auto_flush, bool index_swap>
template <u32 prim, bool auto_flush>
__forceinline void GSState::VertexKick(u32 skip)
{
constexpr u32 n = NumIndicesForPrim(prim);
@ -3673,7 +3666,7 @@ __forceinline void GSState::VertexKick(u32 skip)
if (auto_flush && skip == 0 && m_index.tail > 0 && ((m_vertex.tail + 1) - m_vertex.head) >= n)
{
HandleAutoFlush<prim, index_swap>();
HandleAutoFlush<prim>();
}
u32 head = m_vertex.head;
@ -3835,8 +3828,8 @@ __forceinline void GSState::VertexKick(u32 skip)
m_index.tail += 1;
break;
case GS_LINELIST:
buff[0] = static_cast<u16>(head + (index_swap ? 1 : 0));
buff[1] = static_cast<u16>(head + (index_swap ? 0 : 1));
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
m_vertex.head = head + 2;
m_vertex.next = head + 2;
m_index.tail += 2;
@ -3849,16 +3842,16 @@ __forceinline void GSState::VertexKick(u32 skip)
head = next;
m_vertex.tail = next + 2;
}
buff[0] = static_cast<u16>(head + (index_swap ? 1 : 0));
buff[1] = static_cast<u16>(head + (index_swap ? 0 : 1));
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
m_vertex.head = head + 1;
m_vertex.next = head + 2;
m_index.tail += 2;
break;
case GS_TRIANGLELIST:
buff[0] = static_cast<u16>(head + (index_swap ? 2 : 0));
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
buff[2] = static_cast<u16>(head + (index_swap ? 0 : 2));
buff[2] = static_cast<u16>(head + 2);
m_vertex.head = head + 3;
m_vertex.next = head + 3;
m_index.tail += 3;
@ -3872,18 +3865,18 @@ __forceinline void GSState::VertexKick(u32 skip)
head = next;
m_vertex.tail = next + 3;
}
buff[0] = static_cast<u16>(head + (index_swap ? 2 : 0));
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
buff[2] = static_cast<u16>(head + (index_swap ? 0 : 2));
buff[2] = static_cast<u16>(head + 2);
m_vertex.head = head + 1;
m_vertex.next = head + 3;
m_index.tail += 3;
break;
case GS_TRIANGLEFAN:
// TODO: remove gaps, next == head && head < tail - 3 || next > head && next < tail - 2 (very rare)
buff[0] = static_cast<u16>(index_swap ? (tail - 1) : (head + 0));
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(tail - 2);
buff[2] = static_cast<u16>(index_swap ? (head + 0) : (tail - 1));
buff[2] = static_cast<u16>(tail - 1);
m_vertex.next = tail;
m_index.tail += 3;
break;

View File

@ -38,8 +38,8 @@ private:
void GIFPackedRegHandlerSTQ(const GIFPackedReg* RESTRICT r);
void GIFPackedRegHandlerUV(const GIFPackedReg* RESTRICT r);
void GIFPackedRegHandlerUV_Hack(const GIFPackedReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFPackedRegHandlerXYZF2(const GIFPackedReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFPackedRegHandlerXYZ2(const GIFPackedReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush> void GIFPackedRegHandlerXYZF2(const GIFPackedReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush> void GIFPackedRegHandlerXYZ2(const GIFPackedReg* RESTRICT r);
void GIFPackedRegHandlerFOG(const GIFPackedReg* RESTRICT r);
void GIFPackedRegHandlerA_D(const GIFPackedReg* RESTRICT r);
void GIFPackedRegHandlerNOP(const GIFPackedReg* RESTRICT r);
@ -55,8 +55,8 @@ private:
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8] = {};
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8] = {};
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size);
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size);
template<u32 prim, bool auto_flush> void GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size);
template<u32 prim, bool auto_flush> void GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size);
void GIFPackedRegHandlerNOP(const GIFPackedReg* RESTRICT r, u32 size);
template<int i> void ApplyTEX0(GIFRegTEX0& TEX0);
@ -68,8 +68,8 @@ private:
void GIFRegHandlerST(const GIFReg* RESTRICT r);
void GIFRegHandlerUV(const GIFReg* RESTRICT r);
void GIFRegHandlerUV_Hack(const GIFReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFRegHandlerXYZF2(const GIFReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush, bool index_swap> void GIFRegHandlerXYZ2(const GIFReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush> void GIFRegHandlerXYZF2(const GIFReg* RESTRICT r);
template<u32 prim, u32 adc, bool auto_flush> void GIFRegHandlerXYZ2(const GIFReg* RESTRICT r);
template<int i> void GIFRegHandlerTEX0(const GIFReg* RESTRICT r);
template<int i> void GIFRegHandlerCLAMP(const GIFReg* RESTRICT r);
void GIFRegHandlerFOG(const GIFReg* RESTRICT r);
@ -102,8 +102,7 @@ private:
void GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r);
void GIFRegHandlerHWREG(const GIFReg* RESTRICT r);
template<bool auto_flush, bool index_swap>
void SetPrimHandlers();
template<bool auto_flush> void SetPrimHandlers();
struct GSTransferBuffer
{
@ -171,12 +170,10 @@ protected:
void GrowVertexBuffer();
bool IsAutoFlushDraw(u32 prim);
template<u32 prim, bool index_swap>
void HandleAutoFlush();
template<u32 prim> void HandleAutoFlush();
void CheckCLUTValidity(u32 prim);
template <u32 prim, bool auto_flush, bool index_swap>
void VertexKick(u32 skip);
template <u32 prim, bool auto_flush> void VertexKick(u32 skip);
// following functions need m_vt to be initialized

View File

@ -7,10 +7,10 @@
#include "common/Console.h"
GSVertexTrace::GSVertexTrace(const GSState* state, bool provoking_vertex_first)
GSVertexTrace::GSVertexTrace(const GSState* state)
: m_state(state)
{
MULTI_ISA_SELECT(GSVertexTracePopulateFunctions)(*this, provoking_vertex_first);
MULTI_ISA_SELECT(GSVertexTracePopulateFunctions)(*this);
}
void GSVertexTrace::Update(const void* vertex, const u16* index, int v_count, int i_count, GS_PRIM_CLASS primclass)

View File

@ -15,7 +15,7 @@ class GSState;
class GSVertexTrace;
MULTI_ISA_DEF(class GSVertexTraceFMM;)
MULTI_ISA_DEF(void GSVertexTracePopulateFunctions(GSVertexTrace& vt, bool provoking_vertex_first);)
MULTI_ISA_DEF(void GSVertexTracePopulateFunctions(GSVertexTrace& vt);)
class alignas(32) GSVertexTrace final : public GSAlignedClass<32>
{
@ -63,7 +63,7 @@ public:
GSVector2 m_lod = {}; // x = min, y = max
public:
GSVertexTrace(const GSState* state, bool provoking_vertex_first);
GSVertexTrace(const GSState* state);
void Update(const void* vertex, const u16* index, int v_count, int i_count, GS_PRIM_CLASS primclass);

View File

@ -9,41 +9,36 @@ class CURRENT_ISA::GSVertexTraceFMM
{
static constexpr GSVector4 s_minmax = GSVector4::cxpr(FLT_MAX, -FLT_MAX, 0.f, 0.f);
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool flat_swapped>
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
static void FindMinMax(GSVertexTrace& vt, const void* vertex, const u16* index, int count);
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
static constexpr GSVertexTrace::FindMinMaxPtr GetFMM(bool provoking_vertex_first);
static constexpr GSVertexTrace::FindMinMaxPtr GetFMM();
public:
static void Populate(GSVertexTrace& vt, bool provoking_vertex_first);
static void Populate(GSVertexTrace& vt);
};
MULTI_ISA_UNSHARED_IMPL;
void CURRENT_ISA::GSVertexTracePopulateFunctions(GSVertexTrace& vt, bool provoking_vertex_first)
void CURRENT_ISA::GSVertexTracePopulateFunctions(GSVertexTrace& vt)
{
GSVertexTraceFMM::Populate(vt, provoking_vertex_first);
GSVertexTraceFMM::Populate(vt);
}
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
constexpr GSVertexTrace::FindMinMaxPtr GSVertexTraceFMM::GetFMM(bool provoking_vertex_first)
constexpr GSVertexTrace::FindMinMaxPtr GSVertexTraceFMM::GetFMM()
{
constexpr bool real_iip = primclass == GS_SPRITE_CLASS ? false : iip;
constexpr bool real_fst = tme ? fst : false;
constexpr bool provoking_vertex_first_class = primclass == GS_LINE_CLASS || primclass == GS_TRIANGLE_CLASS;
const bool swap = provoking_vertex_first_class && !iip && provoking_vertex_first;
if (swap)
return FindMinMax<primclass, real_iip, tme, real_fst, color, true>;
else
return FindMinMax<primclass, real_iip, tme, real_fst, color, false>;
return FindMinMax<primclass, real_iip, tme, real_fst, color>;
}
void GSVertexTraceFMM::Populate(GSVertexTrace& vt, bool provoking_vertex_first)
void GSVertexTraceFMM::Populate(GSVertexTrace& vt)
{
#define InitUpdate3(P, IIP, TME, FST, COLOR) \
vt.m_fmm[COLOR][FST][TME][IIP][P] = GetFMM<P, IIP, TME, FST, COLOR>(provoking_vertex_first);
vt.m_fmm[COLOR][FST][TME][IIP][P] = GetFMM<P, IIP, TME, FST, COLOR>();
#define InitUpdate2(P, IIP, TME) \
InitUpdate3(P, IIP, TME, 0, 0) \
@ -63,7 +58,7 @@ void GSVertexTraceFMM::Populate(GSVertexTrace& vt, bool provoking_vertex_first)
InitUpdate(GS_SPRITE_CLASS);
}
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool flat_swapped>
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u16* index, int count)
{
const GSDrawingContext* context = vt.m_state->m_context;
@ -110,7 +105,7 @@ void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u
{
// For even n, we process v1 and v2 of the same prim
// (For odd n, we process one vertex from each of two prims)
GSVector4i c = flat_swapped ? c0 : c1;
GSVector4i c = c1; // second color is provoking in flat-shaded primitives
cmin = cmin.min_u8(c);
cmax = cmax.max_u8(c);
}
@ -196,26 +191,16 @@ void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u
int i = 0;
for (; i < (count - 3); i += 6)
{
processVertices(v[index[i + 0]], v[index[i + 3]], flat_swapped);
processVertices(v[index[i + 0]], v[index[i + 3]], false);
processVertices(v[index[i + 1]], v[index[i + 4]], false);
processVertices(v[index[i + 2]], v[index[i + 5]], !flat_swapped);
processVertices(v[index[i + 2]], v[index[i + 5]], true);
}
if (count & 1)
{
if (flat_swapped)
{
processVertices(v[index[i + 1]], v[index[i + 2]], false);
// Compiler optimizations go!
// (And if they don't, it's only one vertex out of many)
processVertices(v[index[i + 0]], v[index[i + 0]], true);
}
else
{
processVertices(v[index[i + 0]], v[index[i + 1]], false);
// Compiler optimizations go!
// (And if they don't, it's only one vertex out of many)
processVertices(v[index[i + 2]], v[index[i + 2]], true);
}
processVertices(v[index[i + 0]], v[index[i + 1]], false);
// Compiler optimizations go!
// (And if they don't, it's only one vertex out of many)
processVertices(v[index[i + 2]], v[index[i + 2]], true);
}
}
else