This commit is contained in:
gabest
2009-01-25 17:40:01 +00:00
parent d44f844ad8
commit f5457868a6
15 changed files with 1600 additions and 1512 deletions

View File

@@ -201,38 +201,6 @@ protected:
}
}
int DrawAsync(GSRasterizer* r)
{
GSVector4i scissor = GetScissor();
//
int prims = 0;
switch(m_env.PRIM.TYPE)
{
case GPU_POLYGON:
ASSERT(!(m_count % 3));
prims = m_count / 3;
for(int i = 0, j = m_count; i < j; i += 3) r->DrawTriangle(&m_vertices[i], scissor);
break;
case GPU_LINE:
ASSERT(!(m_count & 1));
prims = m_count / 2;
for(int i = 0, j = m_count; i < j; i += 2) r->DrawLine(&m_vertices[i], scissor);
break;
case GPU_SPRITE:
ASSERT(!(m_count & 1));
prims = m_count / 2;
for(int i = 0, j = m_count; i < j; i += 2) r->DrawSprite(&m_vertices[i], scissor, false);
break;
default:
__assume(0);
}
return prims;
}
public:
GPURendererSW(const GPURendererSettings& rs, int threads)
: GPURenderer(rs)

View File

@@ -894,11 +894,11 @@ REG64_(GIFReg, UV)
UINT32 _PAD3:32;
REG_END
// GSState::GIFRegHandlerXYOFFSET will make sure that the _PAD1/2 bits are set to zero
REG64_(GIFReg, XYOFFSET)
UINT32 OFX:16;
UINT32 _PAD1:16;
UINT32 OFY:16;
UINT32 _PAD2:16;
UINT32 OFX; // :16; UINT32 _PAD1:16;
UINT32 OFY; // :16; UINT32 _PAD2:16;
REG_END
REG64_(GIFReg, XYZ)

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,7 @@ union GSScanlineSelector
DWORD abed:2; // 27
DWORD pabe:1; // 29
DWORD rfb:1; // 30
DWORD sprite:1; // 31
};
struct
@@ -59,7 +60,19 @@ union GSScanlineSelector
DWORD dw;
operator DWORD() {return dw & 0x7fffffff;}
operator DWORD() {return dw;}
bool IsSolidRect()
{
return sprite
&& iip == 0
&& tfx == TFX_NONE
&& abe == 255
&& ztst <= 1
&& atst <= 1
&& date == 0
&& fge == 0;
}
};
__declspec(align(16)) struct GSScanlineEnvironment
@@ -90,6 +103,7 @@ __declspec(align(16)) struct GSScanlineEnvironment
struct {GSVector4 z, s, t, q; GSVector4i rb, ga, f, si, ti, _pad[3];} d[4];
struct {GSVector4 z, stq; GSVector4i c, f, st;} d4;
struct {GSVector4i rb, ga;} c;
struct {GSVector4i z, f;} p;
};
__declspec(align(16)) struct GSScanlineParam
@@ -125,6 +139,8 @@ class GSDrawScanline : public GSAlignedClass<16>, public IDrawScanline
GSDrawScanlineMap();
DrawScanlinePtr GetDefaultFunction(DWORD dw);
void PrintStats();
};
GSDrawScanlineMap m_ds;

View File

@@ -42,6 +42,7 @@ struct GSRasterizerStats
template<class T> class GSFunctionMap
{
protected:
struct ActivePtr
{
UINT64 frame, frames;
@@ -53,7 +54,6 @@ template<class T> class GSFunctionMap
CRBMap<DWORD, ActivePtr*> m_map_active;
ActivePtr* m_active;
protected:
virtual T GetDefaultFunction(DWORD sel) = 0;
public:
@@ -118,72 +118,43 @@ public:
}
}
void PrintStats()
virtual void PrintStats()
{
if(FILE* fp = fopen("c:\\1.txt", "w"))
__int64 ttpf = 0;
POSITION pos = m_map_active.GetHeadPosition();
while(pos)
{
POSITION pos = m_map_active.GetHeadPosition();
while(pos)
ActivePtr* p = m_map_active.GetNextValue(pos);
if(p->frames)
{
DWORD sel;
ActivePtr* p;
m_map_active.GetNextAssoc(pos, sel, p);
if(m_map.Lookup(sel))
{
continue;
}
if(p->frames > 30)
{
int tpf = (int)((p->ticks / p->frames) * 10000 / (3000000000 / 60)); // 3 GHz, 60 fps
if(tpf >= 500)
{
_ftprintf(fp, _T("InitDS_Sel(0x%08x); // %6.2f%%\n"), sel, (float)tpf / 100);
}
}
}
fclose(fp);
}
{
__int64 ttpf = 0;
POSITION pos = m_map_active.GetHeadPosition();
while(pos)
{
ActivePtr* p = m_map_active.GetNextValue(pos);
ttpf += p->ticks / p->frames;
}
}
pos = m_map_active.GetHeadPosition();
pos = m_map_active.GetHeadPosition();
while(pos)
while(pos)
{
DWORD sel;
ActivePtr* p;
m_map_active.GetNextAssoc(pos, sel, p);
if(p->frames > 0)
{
DWORD sel;
ActivePtr* p;
__int64 tpp = p->pixels > 0 ? p->ticks / p->pixels : 0;
__int64 tpf = p->frames > 0 ? p->ticks / p->frames : 0;
__int64 ppf = p->frames > 0 ? p->pixels / p->frames : 0;
m_map_active.GetNextAssoc(pos, sel, p);
if(p->frames > 0)
{
__int64 tpp = p->pixels > 0 ? p->ticks / p->pixels : 0;
__int64 tpf = p->frames > 0 ? p->ticks / p->frames : 0;
__int64 ppf = p->frames > 0 ? p->pixels / p->frames : 0;
printf("[%08x]%c %6.2f%% | %5.2f%% | f %4I64d | p %10I64d | tpp %4I64d | tpf %9I64d | ppf %7I64d\n",
sel, !m_map.Lookup(sel) ? '*' : ' ',
(float)(tpf * 10000 / 50000000) / 100,
(float)(tpf * 10000 / ttpf) / 100,
p->frames, p->pixels,
tpp, tpf, ppf);
}
printf("[%08x]%c %6.2f%% | %5.2f%% | f %4I64d | p %10I64d | tpp %4I64d | tpf %9I64d | ppf %7I64d\n",
sel, !m_map.Lookup(sel) ? '*' : ' ',
(float)(tpf * 10000 / 50000000) / 100,
(float)(tpf * 10000 / ttpf) / 100,
p->frames, p->pixels,
tpp, tpf, ppf);
}
}
}

View File

@@ -417,7 +417,7 @@ GSLocalMemory::Offset* GSLocalMemory::GetOffset(DWORD bp, DWORD bw, DWORD psm)
{
if(bw == 0) {ASSERT(0); return NULL;}
ASSERT(m_psm[psm].trbpp > 8); // only for 16/24/32/8h/4hh/4hl formats where all columns are the same
ASSERT(m_psm[psm].bpp > 8); // only for 16/24/32/8h/4hh/4hl formats where all columns are the same
DWORD hash = bp | (bw << 14) | (psm << 20);
@@ -459,11 +459,14 @@ GSLocalMemory::Offset4* GSLocalMemory::GetOffset4(const GIFRegFRAME& FRAME, cons
DWORD zpsm = ZBUF.PSM;
DWORD bw = FRAME.FBW;
ASSERT(m_psm[fpsm].bpp > 8 || m_psm[zpsm].bpp > 8);
ASSERT(m_psm[fpsm].trbpp > 8 || m_psm[zpsm].trbpp > 8);
// "(psm & 3) | (psm >> 3)" makes 4 bit unique identifiers for render target formats (only)
// "(psm & 0x0f) ^ ((psm & 0xf0) >> 2)" creates 4 bit unique identifiers for render target formats (only)
DWORD hash = (FRAME.FBP << 0) | (ZBUF.ZBP << 9) | (bw << 18) | (((fpsm & 3) | (fpsm >> 3)) << 24) | (((zpsm & 3) | (zpsm >> 3)) << 28);
DWORD fpsm_hash = (fpsm & 0x0f) ^ ((fpsm & 0x30) >> 2);
DWORD zpsm_hash = (zpsm & 0x0f) ^ ((zpsm & 0x30) >> 2);
DWORD hash = (FRAME.FBP << 0) | (ZBUF.ZBP << 9) | (bw << 18) | (fpsm_hash << 24) | (zpsm_hash << 28);
if(CRBMap<DWORD, Offset4*>::CPair* pair = m_o4map.Lookup(hash))
{

View File

@@ -480,7 +480,12 @@ protected:
PRIM->TME ? (int)m_context->TEX0.PSM : 0xff);
*/
Draw();
if(GSUtil::EncodePSM(m_context->FRAME.PSM) != 3 && GSUtil::EncodePSM(m_context->ZBUF.PSM) != 3)
{
// FIXME: berserk fpsm = 27 (8H)
Draw();
}
m_count = 0;
}
@@ -493,12 +498,8 @@ protected:
m_maxcount -= 100;
}
template<DWORD prim> void VertexKick(bool skip)
template<DWORD prim> __forceinline Vertex* DrawingKick(bool skip, DWORD& count)
{
(this->*m_fpAddVertexHandlers[PRIM->TME][PRIM->FST])();
DWORD count = 0;
switch(prim)
{
case GS_POINTLIST: count = 1; break;
@@ -514,7 +515,7 @@ protected:
if(m_vl.GetCount() < count)
{
return;
return NULL;
}
if(m_count >= m_maxcount)
@@ -566,30 +567,16 @@ protected:
case GS_INVALID:
ASSERT(0);
m_vl.RemoveAll();
return;
return NULL;
default:
__assume(0);
}
if(!skip)
{
(this->*m_fpAddPrimHandlers[prim])(v, count);
m_count += count;
}
return !skip ? v : NULL;
}
virtual void Draw() = 0;
typedef void (GSRendererT<Device, Vertex>::*AddVertexHandler)();
typedef void (GSRendererT<Device, Vertex>::*AddPrimHandler)(Vertex* v, DWORD& count);
AddVertexHandler m_fpAddVertexHandlers[2][2];
AddPrimHandler m_fpAddPrimHandlers[8];
void AddVertexNull() {ASSERT(0);}
void AddPrimNull(Vertex* v, DWORD& count) {ASSERT(0);}
public:
GSRendererT(BYTE* base, bool mt, void (*irq)(), int nloophack, const GSRendererSettings& rs, bool psrr = true)
: GSRenderer<Device>(base, mt, irq, nloophack, rs, psrr)
@@ -597,24 +584,6 @@ public:
, m_maxcount(0)
, m_vertices(NULL)
{
m_fpVertexKickHandlers[GS_POINTLIST] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_POINTLIST>;
m_fpVertexKickHandlers[GS_LINELIST] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_LINELIST>;
m_fpVertexKickHandlers[GS_LINESTRIP] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_LINESTRIP>;
m_fpVertexKickHandlers[GS_TRIANGLELIST] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_TRIANGLELIST>;
m_fpVertexKickHandlers[GS_TRIANGLESTRIP] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_TRIANGLESTRIP>;
m_fpVertexKickHandlers[GS_TRIANGLEFAN] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_TRIANGLEFAN>;
m_fpVertexKickHandlers[GS_SPRITE] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_SPRITE>;
m_fpVertexKickHandlers[GS_INVALID] = (VertexKickHandler)&GSRendererT<Device, Vertex>::VertexKick<GS_INVALID>;
m_fpAddVertexHandlers[0][0] = &GSRendererT<Device, Vertex>::AddVertexNull;
m_fpAddVertexHandlers[0][1] = &GSRendererT<Device, Vertex>::AddVertexNull;
m_fpAddVertexHandlers[1][0] = &GSRendererT<Device, Vertex>::AddVertexNull;
m_fpAddVertexHandlers[1][1] = &GSRendererT<Device, Vertex>::AddVertexNull;
for(int i = 0; i < countof(m_fpAddPrimHandlers); i++)
{
m_fpAddPrimHandlers[i] = &GSRendererT<Device, Vertex>::AddPrimNull;
}
}
~GSRendererT()

View File

@@ -27,18 +27,7 @@
GSRendererHW10::GSRendererHW10(BYTE* base, bool mt, void (*irq)(), int nloophack, const GSRendererSettings& rs)
: GSRendererHW<Device, Vertex, TextureCache>(base, mt, irq, nloophack, rs, true)
{
m_fpAddVertexHandlers[0][0] = (AddVertexHandler)&GSRendererHW10::AddVertex<0, 0>;
m_fpAddVertexHandlers[0][1] = (AddVertexHandler)&GSRendererHW10::AddVertex<0, 0>;
m_fpAddVertexHandlers[1][0] = (AddVertexHandler)&GSRendererHW10::AddVertex<1, 0>;
m_fpAddVertexHandlers[1][1] = (AddVertexHandler)&GSRendererHW10::AddVertex<1, 1>;
m_fpAddPrimHandlers[GS_POINTLIST] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_POINT_CLASS>;
m_fpAddPrimHandlers[GS_LINELIST] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_LINESTRIP] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLELIST] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLESTRIP] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLEFAN] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_SPRITE] = (AddPrimHandler)&GSRendererHW10::AddPrim<GS_SPRITE_CLASS>;
InitVertexKick<GSRendererHW10>();
}
bool GSRendererHW10::Create(LPCTSTR title)
@@ -81,8 +70,8 @@ bool GSRendererHW10::Create(LPCTSTR title)
return true;
}
template<DWORD tme, DWORD fst>
void GSRendererHW10::AddVertex()
template<DWORD prim, DWORD tme, DWORD fst>
void GSRendererHW10::VertexKick(bool skip)
{
Vertex& dst = m_vl.AddTail();
@@ -93,86 +82,91 @@ void GSRendererHW10::AddVertex()
{
GSVector4::storel(&dst.ST, m_v.GetUV());
}
}
template<int primclass>
void GSRendererHW10::AddPrim(Vertex* v, DWORD& count)
{
GSVector4i scissor = m_context->scissor.dx10;
#if _M_SSE >= 0x401
GSVector4i pmin, pmax, v0, v1, v2;
switch(primclass)
DWORD count = 0;
if(Vertex* v = DrawingKick<prim>(skip, count))
{
case GS_POINT_CLASS:
v0 = GSVector4i::load((int)v[0].p.xy).upl16();
pmin = v0;
pmax = v0;
break;
case GS_LINE_CLASS:
case GS_SPRITE_CLASS:
v0 = GSVector4i::load((int)v[0].p.xy);
v1 = GSVector4i::load((int)v[1].p.xy);
pmin = v0.min_u16(v1).upl16();
pmax = v0.max_u16(v1).upl16();
break;
case GS_TRIANGLE_CLASS:
v0 = GSVector4i::load((int)v[0].p.xy);
v1 = GSVector4i::load((int)v[1].p.xy);
v2 = GSVector4i::load((int)v[2].p.xy);
pmin = v0.min_u16(v1).min_u16(v2).upl16();
pmax = v0.max_u16(v1).max_u16(v2).upl16();
break;
}
GSVector4i scissor = m_context->scissor.dx10;
GSVector4i test = (pmax < scissor) | (pmin > scissor.zwxy());
#if _M_SSE >= 0x401
if(test.mask() & 0xff)
{
count = 0;
return;
}
GSVector4i pmin, pmax, v0, v1, v2;
#else
switch(primclass)
{
case GS_POINT_CLASS:
if(v[0].p.x < scissor.x
|| v[0].p.x > scissor.z
|| v[0].p.y < scissor.y
|| v[0].p.y > scissor.w)
switch(prim)
{
case GS_POINTLIST:
v0 = GSVector4i::load((int)v[0].p.xy).upl16();
pmin = v0;
pmax = v0;
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
v0 = GSVector4i::load((int)v[0].p.xy);
v1 = GSVector4i::load((int)v[1].p.xy);
pmin = v0.min_u16(v1).upl16();
pmax = v0.max_u16(v1).upl16();
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
v0 = GSVector4i::load((int)v[0].p.xy);
v1 = GSVector4i::load((int)v[1].p.xy);
v2 = GSVector4i::load((int)v[2].p.xy);
pmin = v0.min_u16(v1).min_u16(v2).upl16();
pmax = v0.max_u16(v1).max_u16(v2).upl16();
break;
}
GSVector4i test = (pmax < scissor) | (pmin > scissor.zwxy());
if(test.mask() & 0xff)
{
count = 0;
return;
}
break;
case GS_LINE_CLASS:
case GS_SPRITE_CLASS:
if(v[0].p.x < scissor.x && v[1].p.x < scissor.x
|| v[0].p.x > scissor.z && v[1].p.x > scissor.z
|| v[0].p.y < scissor.y && v[1].p.y < scissor.y
|| v[0].p.y > scissor.w && v[1].p.y > scissor.w)
{
count = 0;
return;
}
break;
case GS_TRIANGLE_CLASS:
if(v[0].p.x < scissor.x && v[1].p.x < scissor.x && v[2].p.x < scissor.x
|| v[0].p.x > scissor.z && v[1].p.x > scissor.z && v[2].p.x > scissor.z
|| v[0].p.y < scissor.y && v[1].p.y < scissor.y && v[2].p.y < scissor.y
|| v[0].p.y > scissor.w && v[1].p.y > scissor.w && v[2].p.y > scissor.w)
{
count = 0;
return;
}
break;
}
#endif
#else
switch(prim)
{
case GS_POINTLIST:
if(v[0].p.x < scissor.x
|| v[0].p.x > scissor.z
|| v[0].p.y < scissor.y
|| v[0].p.y > scissor.w)
{
return;
}
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
if(v[0].p.x < scissor.x && v[1].p.x < scissor.x
|| v[0].p.x > scissor.z && v[1].p.x > scissor.z
|| v[0].p.y < scissor.y && v[1].p.y < scissor.y
|| v[0].p.y > scissor.w && v[1].p.y > scissor.w)
{
return;
}
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
if(v[0].p.x < scissor.x && v[1].p.x < scissor.x && v[2].p.x < scissor.x
|| v[0].p.x > scissor.z && v[1].p.x > scissor.z && v[2].p.x > scissor.z
|| v[0].p.y < scissor.y && v[1].p.y < scissor.y && v[2].p.y < scissor.y
|| v[0].p.y > scissor.w && v[1].p.y > scissor.w && v[2].p.y > scissor.w)
{
return;
}
break;
}
#endif
m_count += count;
}
}
void GSRendererHW10::Draw(int prim, Texture& rt, Texture& ds, GSTextureCache<Device>::GSTexture* tex)

View File

@@ -32,17 +32,11 @@ class GSRendererHW10 : public GSRendererHW<GSDevice10, GSVertexHW10, GSTextureCa
typedef GSVertexHW10 Vertex;
typedef GSTextureCache10 TextureCache;
bool WrapZ(DWORD maxz);
protected:
GSTextureFX10 m_tfx;
template<DWORD tme, DWORD fst>
void AddVertex();
template<int primclass>
void AddPrim(Vertex* v, DWORD& count);
bool WrapZ(DWORD maxz);
void Draw(int prim, Texture& rt, Texture& ds, GSTextureCache<Device>::GSTexture* tex);
struct
@@ -57,4 +51,6 @@ public:
GSRendererHW10(BYTE* base, bool mt, void (*irq)(), int nloophack, const GSRendererSettings& rs);
bool Create(LPCTSTR title);
template<DWORD prim, DWORD tme, DWORD fst> void VertexKick(bool skip);
};

View File

@@ -30,18 +30,7 @@ GSRendererHW9::GSRendererHW9(BYTE* base, bool mt, void (*irq)(), int nloophack,
m_fba.enabled = !!AfxGetApp()->GetProfileInt(_T("Settings"), _T("fba"), TRUE);
m_logz = !!AfxGetApp()->GetProfileInt(_T("Settings"), _T("logz"), FALSE);
m_fpAddVertexHandlers[0][0] = (AddVertexHandler)&GSRendererHW9::AddVertex<0, 0>;
m_fpAddVertexHandlers[0][1] = (AddVertexHandler)&GSRendererHW9::AddVertex<0, 0>;
m_fpAddVertexHandlers[1][0] = (AddVertexHandler)&GSRendererHW9::AddVertex<1, 0>;
m_fpAddVertexHandlers[1][1] = (AddVertexHandler)&GSRendererHW9::AddVertex<1, 1>;
m_fpAddPrimHandlers[GS_POINTLIST] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_POINT_CLASS>;
m_fpAddPrimHandlers[GS_LINELIST] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_LINESTRIP] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLELIST] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLESTRIP] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLEFAN] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_SPRITE] = (AddPrimHandler)&GSRendererHW9::AddPrim<GS_SPRITE_CLASS>;
InitVertexKick<GSRendererHW9>();
}
bool GSRendererHW9::Create(LPCTSTR title)
@@ -85,8 +74,8 @@ bool GSRendererHW9::Create(LPCTSTR title)
return true;
}
template<DWORD tme, DWORD fst>
void GSRendererHW9::AddVertex()
template<DWORD prim, DWORD tme, DWORD fst>
void GSRendererHW9::VertexKick(bool skip)
{
Vertex& dst = m_vl.AddTail();
@@ -110,65 +99,73 @@ void GSRendererHW9::AddVertex()
dst.p.w = m_v.RGBAQ.Q;
}
}
}
template<int primclass>
void GSRendererHW9::AddPrim(Vertex* v, DWORD& count)
{
GSVector4 scissor = m_context->scissor.dx9;
GSVector4 pmin, pmax;
switch(primclass)
DWORD count = 0;
if(Vertex* v = DrawingKick<prim>(skip, count))
{
case GS_POINT_CLASS:
pmin = v[0].p;
pmax = v[0].p;
break;
case GS_LINE_CLASS:
case GS_SPRITE_CLASS:
pmin = v[0].p.minv(v[1].p);
pmax = v[0].p.maxv(v[1].p);
break;
case GS_TRIANGLE_CLASS:
pmin = v[0].p.minv(v[1].p).minv(v[2].p);
pmax = v[0].p.maxv(v[1].p).maxv(v[2].p);
break;
}
GSVector4 scissor = m_context->scissor.dx9;
GSVector4 test = (pmax < scissor) | (pmin > scissor.zwxy());
GSVector4 pmin, pmax;
if(test.mask() & 3)
{
count = 0;
return;
}
switch(prim)
{
case GS_POINTLIST:
pmin = v[0].p;
pmax = v[0].p;
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
pmin = v[0].p.minv(v[1].p);
pmax = v[0].p.maxv(v[1].p);
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
pmin = v[0].p.minv(v[1].p).minv(v[2].p);
pmax = v[0].p.maxv(v[1].p).maxv(v[2].p);
break;
}
switch(primclass)
{
case GS_POINT_CLASS:
break;
case GS_LINE_CLASS:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0;}
break;
case GS_SPRITE_CLASS:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0;}
v[0].p.z = v[1].p.z;
v[0].p.w = v[1].p.w;
v[0].c1 = v[1].c1;
v[2] = v[1];
v[3] = v[1];
v[1].p.y = v[0].p.y;
v[1].t.y = v[0].t.y;
v[2].p.x = v[0].p.x;
v[2].t.x = v[0].t.x;
v[4] = v[1];
v[5] = v[2];
count += 4;
break;
case GS_TRIANGLE_CLASS:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0 = v[2].c0;}
break;
GSVector4 test = (pmax < scissor) | (pmin > scissor.zwxy());
if(test.mask() & 3)
{
return;
}
switch(prim)
{
case GS_POINTLIST:
break;
case GS_LINELIST:
case GS_LINESTRIP:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0;}
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0 = v[2].c0;}
break;
case GS_SPRITE:
if(PRIM->IIP == 0) {v[0].c0 = v[1].c0;}
v[0].p.z = v[1].p.z;
v[0].p.w = v[1].p.w;
v[0].c1 = v[1].c1;
v[2] = v[1];
v[3] = v[1];
v[1].p.y = v[0].p.y;
v[1].t.y = v[0].t.y;
v[2].p.x = v[0].p.x;
v[2].t.x = v[0].t.x;
v[4] = v[1];
v[5] = v[2];
count += 4;
break;
}
m_count += count;
}
}

View File

@@ -32,18 +32,12 @@ class GSRendererHW9 : public GSRendererHW<GSDevice9, GSVertexHW9, GSTextureCache
typedef GSVertexHW9 Vertex;
typedef GSTextureCache9 TextureCache;
bool WrapZ(float maxz);
protected:
GSTextureFX9 m_tfx;
bool m_logz;
template<DWORD tme, DWORD fst>
void AddVertex();
template<int primclass>
void AddPrim(Vertex* v, DWORD& count);
bool WrapZ(float maxz);
void Draw(int prim, Texture& rt, Texture& ds, GSTextureCache<Device>::GSTexture* tex);
struct
@@ -66,4 +60,6 @@ public:
GSRendererHW9(BYTE* base, bool mt, void (*irq)(), int nloophack, const GSRendererSettings& rs);
bool Create(LPCTSTR title);
template<DWORD prim, DWORD tme, DWORD fst> void VertexKick(bool skip);
};

View File

@@ -27,15 +27,6 @@
template<class Device> class GSRendererNull : public GSRendererT<Device, GSVertexNull>
{
protected:
void AddVertex()
{
}
void AddPrim(GSVertexNull* v, DWORD& count)
{
m_perfmon.Put(GSPerfMon::Prim, 1);
}
void Draw()
{
}
@@ -49,14 +40,10 @@ public:
GSRendererNull(BYTE* base, bool mt, void (*irq)(), int nloophack, const GSRendererSettings& rs)
: GSRendererT<Device, GSVertexNull>(base, mt, irq, nloophack, rs)
{
m_fpAddVertexHandlers[0][0] = (AddVertexHandler)&GSRendererNull::AddVertex;
m_fpAddVertexHandlers[0][1] = (AddVertexHandler)&GSRendererNull::AddVertex;
m_fpAddVertexHandlers[1][0] = (AddVertexHandler)&GSRendererNull::AddVertex;
m_fpAddVertexHandlers[1][1] = (AddVertexHandler)&GSRendererNull::AddVertex;
InitVertexKick<GSRendererNull<Device> >();
}
for(int i = 0; i < countof(m_fpAddPrimHandlers); i++)
{
m_fpAddPrimHandlers[i] = (AddPrimHandler)&GSRendererNull::AddPrim;
}
template<DWORD prim, DWORD tme, DWORD fst> void VertexKick(bool skip)
{
}
};

View File

@@ -117,127 +117,6 @@ protected:
return true;
}
template<DWORD tme, DWORD fst> void AddVertex()
{
GSVertexSW v;
GSVector4i p((int)m_v.XYZ.X, (int)m_v.XYZ.Y, 0, (int)m_v.FOG.F);
GSVector4i o((int)m_context->XYOFFSET.OFX, (int)m_context->XYOFFSET.OFY);
v.p = GSVector4(p - o) * g_pos_scale;
v.c = GSVector4(GSVector4i::load((int)m_v.RGBAQ.ai32[0]).u8to32() << 7);
if(tme)
{
float q;
if(fst)
{
v.t = GSVector4(GSVector4i(m_v.UV.U, m_v.UV.V) << (16 - 4));
q = 1.0f;
}
else
{
v.t = GSVector4(m_v.ST.S, m_v.ST.T);
v.t *= GSVector4(0x10000 << m_context->TEX0.TW, 0x10000 << m_context->TEX0.TH);
q = m_v.RGBAQ.Q;
}
v.t = v.t.xyxy(GSVector4::load(q));
}
GSVertexSW& dst = m_vl.AddTail();
dst = v;
dst.p.z = (float)min(m_v.XYZ.Z, 0xffffff00); // max value which can survive the DWORD => float => DWORD conversion
}
template<int primclass> void AddPrim(GSVertexSW* v, DWORD& count)
{
GSVector4 pmin, pmax;
switch(primclass)
{
case GS_POINT_CLASS:
pmin = v[0].p;
pmax = v[0].p;
break;
case GS_LINE_CLASS:
pmin = v[0].p.minv(v[1].p);
pmax = v[0].p.maxv(v[1].p);
break;
case GS_TRIANGLE_CLASS:
pmin = v[0].p.minv(v[1].p).minv(v[2].p);
pmax = v[0].p.maxv(v[1].p).maxv(v[2].p);
break;
case GS_SPRITE_CLASS:
pmin = v[0].p.minv(v[1].p);
pmax = v[0].p.maxv(v[1].p);
break;
}
GSVector4 scissor = m_context->scissor.ex;
GSVector4 test = (pmax < scissor) | (pmin > scissor.zwxy());
if(primclass != GS_POINT_CLASS && primclass != GS_LINE_CLASS)
{
test |= pmin.ceil() == pmax.ceil();
}
if(test.mask() & 3)
{
count = 0;
return;
}
m_vtrace.min.p = m_vtrace.min.p.minv(pmin);
m_vtrace.max.p = m_vtrace.max.p.maxv(pmax);
switch(primclass)
{
case GS_POINT_CLASS:
m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t);
m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c);
break;
case GS_LINE_CLASS:
if(PRIM->IIP == 0) {v[0].c = v[1].c;}
m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t);
m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c).minv(v[1].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c).maxv(v[1].c);
break;
case GS_TRIANGLE_CLASS:
if(PRIM->IIP == 0) {v[0].c = v[2].c; v[1].c = v[2].c;}
m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t.minv(v[2].t));
m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t.maxv(v[2].t));
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c).minv(v[1].c.minv(v[2].c));
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c).maxv(v[1].c.maxv(v[2].c));
break;
case GS_SPRITE_CLASS:
m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t);
m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[1].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[1].c);
break;
}
}
GSVector4i GetScissor()
{
GSVector4i v = GSVector4i(m_context->scissor.in);
// TODO: find a game that overflows and check which one is the right behaviour
v.z = min(v.z, (int)m_context->FRAME.FBW * 64);
return v;
}
bool TryAlphaTest(DWORD& fm, DWORD& zm)
{
const GSDrawingEnvironment& env = m_env;
@@ -388,6 +267,7 @@ protected:
p.sel.atst = ATST_ALWAYS;
p.sel.tfx = TFX_NONE;
p.sel.abe = 255;
p.sel.sprite = primclass == GS_SPRITE_CLASS ? 1 : 0;
p.fm = context->FRAME.FBMSK;
p.zm = context->ZBUF.ZMSK || context->TEST.ZTE == 0 ? 0xffffffff : 0;
@@ -625,7 +505,8 @@ protected:
GSRasterizerData data;
data.scissor = GetScissor();
data.scissor = GSVector4i(m_context->scissor.in);
data.scissor.z = min(data.scissor.z, (int)m_context->FRAME.FBW * 64); // TODO: find a game that overflows and check which one is the right behaviour
data.primclass = GSUtil::GetPrimClass(PRIM->PRIM);
data.vertices = m_vertices;
data.count = m_count;
@@ -813,22 +694,138 @@ public:
m_tc = new GSTextureCacheSW(this);
m_fpAddVertexHandlers[0][0] = (AddVertexHandler)&GSRendererSW::AddVertex<0, 0>;
m_fpAddVertexHandlers[0][1] = (AddVertexHandler)&GSRendererSW::AddVertex<0, 0>;
m_fpAddVertexHandlers[1][0] = (AddVertexHandler)&GSRendererSW::AddVertex<1, 0>;
m_fpAddVertexHandlers[1][1] = (AddVertexHandler)&GSRendererSW::AddVertex<1, 1>;
m_fpAddPrimHandlers[GS_POINTLIST] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_POINT_CLASS>;
m_fpAddPrimHandlers[GS_LINELIST] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_LINESTRIP] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_LINE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLELIST] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLESTRIP] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_TRIANGLEFAN] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_TRIANGLE_CLASS>;
m_fpAddPrimHandlers[GS_SPRITE] = (AddPrimHandler)&GSRendererSW::AddPrim<GS_SPRITE_CLASS>;
InitVertexKick<GSRendererSW<Device> >();
}
virtual ~GSRendererSW()
{
delete m_tc;
}
template<DWORD prim, DWORD tme, DWORD fst>
void VertexKick(bool skip)
{
const GSDrawingContext* context = m_context;
GSVector4i xy = GSVector4i::load((int)m_v.XYZ.ai32[0]);
xy = xy.insert16<3>(m_v.FOG.F);
xy = xy.upl16();
xy -= context->XYOFFSET;
GSVertexSW v;
v.p = GSVector4(xy) * g_pos_scale;
v.c = GSVector4(GSVector4i::load((int)m_v.RGBAQ.ai32[0]).u8to32() << 7);
if(tme)
{
float q;
if(fst)
{
v.t = GSVector4(((GSVector4i)m_v.UV).upl16() << (16 - 4));
q = 1.0f;
}
else
{
v.t = GSVector4(m_v.ST.S, m_v.ST.T);
v.t *= GSVector4(0x10000 << context->TEX0.TW, 0x10000 << context->TEX0.TH);
q = m_v.RGBAQ.Q;
}
v.t = v.t.xyxy(GSVector4::load(q));
}
GSVertexSW& dst = m_vl.AddTail();
dst = v;
dst.p.z = (float)min(m_v.XYZ.Z, 0xffffff00); // max value which can survive the DWORD => float => DWORD conversion
DWORD count = 0;
if(GSVertexSW* v = DrawingKick<prim>(skip, count))
{
GSVector4 pmin, pmax;
switch(prim)
{
case GS_POINTLIST:
pmin = v[0].p;
pmax = v[0].p;
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
pmin = v[0].p.minv(v[1].p);
pmax = v[0].p.maxv(v[1].p);
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
pmin = v[0].p.minv(v[1].p).minv(v[2].p);
pmax = v[0].p.maxv(v[1].p).maxv(v[2].p);
break;
}
GSVector4 scissor = context->scissor.ex;
GSVector4 test = (pmax < scissor) | (pmin > scissor.zwxy());
switch(prim)
{
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
case GS_SPRITE:
test |= pmin.ceil() == pmax.ceil();
break;
}
if(test.mask() & 3)
{
return;
}
m_vtrace.min.p = m_vtrace.min.p.minv(pmin);
m_vtrace.max.p = m_vtrace.max.p.maxv(pmax);
switch(prim)
{
case GS_POINTLIST:
if(tme) m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t);
if(tme) m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c);
break;
case GS_LINELIST:
case GS_LINESTRIP:
if(PRIM->IIP == 0) {v[0].c = v[1].c;}
if(tme) m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t);
if(tme) m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c).minv(v[1].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c).maxv(v[1].c);
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
if(PRIM->IIP == 0) {v[0].c = v[2].c; v[1].c = v[2].c;}
if(tme) m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t.minv(v[2].t));
if(tme) m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t.maxv(v[2].t));
m_vtrace.min.c = m_vtrace.min.c.minv(v[0].c).minv(v[1].c.minv(v[2].c));
m_vtrace.max.c = m_vtrace.max.c.maxv(v[0].c).maxv(v[1].c.maxv(v[2].c));
break;
case GS_SPRITE:
if(tme) m_vtrace.min.t = m_vtrace.min.t.minv(v[0].t).minv(v[1].t);
if(tme) m_vtrace.max.t = m_vtrace.max.t.maxv(v[0].t).maxv(v[1].t);
m_vtrace.min.c = m_vtrace.min.c.minv(v[1].c);
m_vtrace.max.c = m_vtrace.max.c.maxv(v[1].c);
break;
}
m_count += count;
}
}
};

View File

@@ -34,6 +34,7 @@ GSState::GSState(BYTE* base, bool mt, void (*irq)(), int nloophack)
, m_vprim(1)
, m_version(5)
, m_frameskip(0)
, m_vkf(NULL)
{
m_sssize = 0;
@@ -516,6 +517,8 @@ void GSState::GIFRegHandlerPRIM(GIFReg* r)
m_context = &m_env.CTXT[PRIM->CTXT];
UpdateVertexKick();
ResetPrim();
}
@@ -649,12 +652,14 @@ template<int i> void GSState::GIFRegHandlerTEX2(GIFReg* r)
template<int i> void GSState::GIFRegHandlerXYOFFSET(GIFReg* r)
{
if(!(m_env.CTXT[i].XYOFFSET == (GSVector4i)r->XYOFFSET).alltrue())
GSVector4i o = (GSVector4i)r->XYOFFSET & GSVector4i::x0000ffff();
if(!(m_env.CTXT[i].XYOFFSET == o).alltrue())
{
Flush();
}
m_env.CTXT[i].XYOFFSET = (GSVector4i)r->XYOFFSET;
m_env.CTXT[i].XYOFFSET = o;
m_env.CTXT[i].UpdateScissor();
}
@@ -673,6 +678,8 @@ void GSState::GIFRegHandlerPRMODECONT(GIFReg* r)
if(PRIM->PRIM == 7) TRACE(_T("Invalid PRMODECONT/PRIM\n"));
m_context = &m_env.CTXT[PRIM->CTXT];
UpdateVertexKick();
}
void GSState::GIFRegHandlerPRMODE(GIFReg* r)
@@ -687,6 +694,8 @@ void GSState::GIFRegHandlerPRMODE(GIFReg* r)
m_env.PRMODE._PRIM = _PRIM;
m_context = &m_env.CTXT[PRIM->CTXT];
UpdateVertexKick();
}
void GSState::GIFRegHandlerTEXCLUT(GIFReg* r)
@@ -956,9 +965,9 @@ void GSState::GIFRegHandlerTRXDIR(GIFReg* r)
void GSState::GIFRegHandlerHWREG(GIFReg* r)
{
// TODO
ASSERT(m_env.TRXDIR.XDIR == 0); // host => local
ASSERT(0);
Write((BYTE*)r, 8); // hunting ground
}
void GSState::GIFRegHandlerSIGNAL(GIFReg* r)
@@ -1221,7 +1230,7 @@ template<int index> void GSState::Transfer(BYTE* mem, UINT32 size)
if(path.tag.PRE)
{
ASSERT(path.tag.FLG != GIF_FLG_IMAGE); // kingdom hearts, ffxii, tales of abyss
ASSERT(path.tag.FLG != GIF_FLG_IMAGE); // kingdom hearts, ffxii, tales of abyss, berserk
if((path.tag.FLG & 2) == 0)
{
@@ -1530,6 +1539,9 @@ int GSState::Defrost(const GSFreezeData* fd)
ReadState(&m_env.CTXT[i].FRAME, data);
ReadState(&m_env.CTXT[i].ZBUF, data);
m_env.CTXT[i].XYOFFSET.OFX &= 0xffff;
m_env.CTXT[i].XYOFFSET.OFY &= 0xffff;
if(version <= 4)
{
data += sizeof(DWORD) * 7; // skip
@@ -1559,6 +1571,8 @@ int GSState::Defrost(const GSFreezeData* fd)
m_context = &m_env.CTXT[PRIM->CTXT];
UpdateVertexKick();
m_env.CTXT[0].UpdateScissor();
m_env.CTXT[1].UpdateScissor();

View File

@@ -125,13 +125,59 @@ class GSState : public GSAlignedClass<16>
protected:
bool IsBadFrame(int& skip);
typedef void (GSState::*VertexKickHandler)(bool skip);
typedef void (GSState::*VertexKickPtr)(bool skip);
VertexKickHandler m_fpVertexKickHandlers[8];
VertexKickPtr m_vk[8][2][2];
VertexKickPtr m_vkf;
template<class T> void InitVertexKick()
{
m_vk[GS_POINTLIST][0][0] = (VertexKickPtr)&T::VertexKick<GS_POINTLIST, 0, 0>;
m_vk[GS_POINTLIST][0][1] = (VertexKickPtr)&T::VertexKick<GS_POINTLIST, 0, 0>;
m_vk[GS_POINTLIST][1][0] = (VertexKickPtr)&T::VertexKick<GS_POINTLIST, 1, 0>;
m_vk[GS_POINTLIST][1][1] = (VertexKickPtr)&T::VertexKick<GS_POINTLIST, 1, 1>;
m_vk[GS_LINELIST][0][0] = (VertexKickPtr)&T::VertexKick<GS_LINELIST, 0, 0>;
m_vk[GS_LINELIST][0][1] = (VertexKickPtr)&T::VertexKick<GS_LINELIST, 0, 0>;
m_vk[GS_LINELIST][1][0] = (VertexKickPtr)&T::VertexKick<GS_LINELIST, 1, 0>;
m_vk[GS_LINELIST][1][1] = (VertexKickPtr)&T::VertexKick<GS_LINELIST, 1, 1>;
m_vk[GS_LINESTRIP][0][0] = (VertexKickPtr)&T::VertexKick<GS_LINESTRIP, 0, 0>;
m_vk[GS_LINESTRIP][0][1] = (VertexKickPtr)&T::VertexKick<GS_LINESTRIP, 0, 0>;
m_vk[GS_LINESTRIP][1][0] = (VertexKickPtr)&T::VertexKick<GS_LINESTRIP, 1, 0>;
m_vk[GS_LINESTRIP][1][1] = (VertexKickPtr)&T::VertexKick<GS_LINESTRIP, 1, 1>;
m_vk[GS_TRIANGLELIST][0][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLELIST, 0, 0>;
m_vk[GS_TRIANGLELIST][0][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLELIST, 0, 0>;
m_vk[GS_TRIANGLELIST][1][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLELIST, 1, 0>;
m_vk[GS_TRIANGLELIST][1][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLELIST, 1, 1>;
m_vk[GS_TRIANGLESTRIP][0][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLESTRIP, 0, 0>;
m_vk[GS_TRIANGLESTRIP][0][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLESTRIP, 0, 0>;
m_vk[GS_TRIANGLESTRIP][1][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLESTRIP, 1, 0>;
m_vk[GS_TRIANGLESTRIP][1][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLESTRIP, 1, 1>;
m_vk[GS_TRIANGLEFAN][0][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLEFAN, 0, 0>;
m_vk[GS_TRIANGLEFAN][0][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLEFAN, 0, 0>;
m_vk[GS_TRIANGLEFAN][1][0] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLEFAN, 1, 0>;
m_vk[GS_TRIANGLEFAN][1][1] = (VertexKickPtr)&T::VertexKick<GS_TRIANGLEFAN, 1, 1>;
m_vk[GS_SPRITE][0][0] = (VertexKickPtr)&T::VertexKick<GS_SPRITE, 0, 0>;
m_vk[GS_SPRITE][0][1] = (VertexKickPtr)&T::VertexKick<GS_SPRITE, 0, 0>;
m_vk[GS_SPRITE][1][0] = (VertexKickPtr)&T::VertexKick<GS_SPRITE, 1, 0>;
m_vk[GS_SPRITE][1][1] = (VertexKickPtr)&T::VertexKick<GS_SPRITE, 1, 1>;
}
void UpdateVertexKick()
{
m_vkf = m_vk[PRIM->PRIM][PRIM->TME][PRIM->FST];
ASSERT(m_vkf != NULL);
}
void VertexKick(bool skip)
{
(this->*m_fpVertexKickHandlers[PRIM->PRIM])(skip);
(this->*m_vkf)(skip);
}
public: