SoftGPU: Implement CONTINUE prim type.

Matches tests (except 3D lines, which aren't implemented yet.)
This commit is contained in:
Unknown W. Brackets 2017-04-29 20:36:36 -07:00
parent ee68f09be5
commit 1666586869
3 changed files with 62 additions and 34 deletions

View file

@ -364,7 +364,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_PRIM:
{
u32 count = data & 0xFFFF;
u32 type = data >> 16;
// Upper bits are ignored.
GEPrimitiveType type = static_cast<GEPrimitiveType>((data >> 16) & 7);
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
ERROR_LOG_REPORT(G3D, "Software: Bad vertex address %08x!", gstate_c.vertexAddr);

View file

@ -212,7 +212,7 @@ struct SplinePatch {
int pad[3];
};
void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type, int vertex_count, u32 vertex_type, int *bytesRead)
void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead)
{
// TODO: Cache VertexDecoder objects
VertexDecoder vdecoder;
@ -239,17 +239,27 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
VertexReader vreader(buf, vtxfmt, vertex_type);
const int max_vtcs_per_prim = 3;
int vtcs_per_prim = 0;
static VertexData data[max_vtcs_per_prim]{};
// This is the index of the next vert in data (or higher, may need modulus.)
static int data_index = 0;
static GEPrimitiveType prev_prim = GE_PRIM_POINTS;
if (prim_type != GE_PRIM_KEEP_PREVIOUS) {
data_index = 0;
prev_prim = prim_type;
} else {
prim_type = prev_prim;
}
int vtcs_per_prim;
switch (prim_type) {
case GE_PRIM_POINTS: vtcs_per_prim = 1; break;
case GE_PRIM_LINES: vtcs_per_prim = 2; break;
case GE_PRIM_TRIANGLES: vtcs_per_prim = 3; break;
case GE_PRIM_RECTANGLES: vtcs_per_prim = 2; break;
default: vtcs_per_prim = 0; break;
}
VertexData data[max_vtcs_per_prim];
// TODO: Do this in two passes - first process the vertices (before indexing/stripping),
// then resolve the indices. This lets us avoid transforming shared vertices twice.
@ -259,19 +269,23 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
case GE_PRIM_TRIANGLES:
case GE_PRIM_RECTANGLES:
{
for (int vtx = 0; vtx < vertex_count; vtx += vtcs_per_prim) {
for (int i = 0; i < vtcs_per_prim; ++i) {
if (indices) {
vreader.Goto(idxConv.convert(vtx + i) - index_lower_bound);
} else {
vreader.Goto(vtx + i);
}
data[i] = ReadVertex(vreader);
if (outside_range_flag)
break;
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices) {
vreader.Goto(idxConv.convert(vtx) - index_lower_bound);
} else {
vreader.Goto(vtx);
}
data[data_index++] = ReadVertex(vreader);
if (data_index < vtcs_per_prim) {
// Keep reading. Note: an incomplete prim will stay read for GE_PRIM_KEEP_PREVIOUS.
continue;
}
// Okay, we've got enough verts. Reset the index for next time.
data_index = 0;
if (outside_range_flag) {
// Cull the prim if it was outside, and move to the next prim.
outside_range_flag = false;
continue;
}
@ -282,10 +296,11 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if (!gstate.getCullMode())
} else if (!gstate.getCullMode()) {
Clipper::ProcessTriangle(data[2], data[1], data[0]);
else
} else {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
}
break;
}
@ -307,7 +322,9 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
case GE_PRIM_LINE_STRIP:
{
int skip_count = 1; // Don't draw a line when loading the first vertex
// Don't draw a line when loading the first vertex.
// If data_index is 1 or 2, etc., it means we're continuing a line strip.
int skip_count = data_index == 0 ? 1 : 0;
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices) {
vreader.Goto(idxConv.convert(vtx) - index_lower_bound);
@ -315,7 +332,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
vreader.Goto(vtx);
}
data[vtx & 1] = ReadVertex(vreader);
data[(data_index++) & 1] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
@ -326,7 +343,8 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
if (skip_count) {
--skip_count;
} else {
Clipper::ProcessLine(data[(vtx & 1) ^ 1], data[vtx & 1]);
// We already incremented data_index, so data_index & 1 is previous one.
Clipper::ProcessLine(data[data_index & 1], data[(data_index & 1) ^ 1]);
}
}
break;
@ -334,7 +352,8 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
case GE_PRIM_TRIANGLE_STRIP:
{
int skip_count = 2; // Don't draw a triangle when loading the first two vertices
// Don't draw a triangle when loading the first two vertices.
int skip_count = data_index >= 2 ? 0 : 2 - data_index;
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices) {
@ -343,7 +362,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
vreader.Goto(vtx);
}
data[vtx % 3] = ReadVertex(vreader);
data[(data_index++) % 3] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
@ -359,7 +378,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
} else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);
@ -372,23 +391,31 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
case GE_PRIM_TRIANGLE_FAN:
{
unsigned int skip_count = 1; // Don't draw a triangle when loading the first two vertices
// Don't draw a triangle when loading the first two vertices.
// (this doesn't count the central one.)
int skip_count = data_index <= 1 ? 1 : 0;
int start_vtx = 0;
if (indices) {
vreader.Goto(idxConv.convert(0) - index_lower_bound);
} else {
vreader.Goto(0);
// Only read the central vertex if we're not continuing.
if (data_index == 0) {
if (indices) {
vreader.Goto(idxConv.convert(0) - index_lower_bound);
} else {
vreader.Goto(0);
}
data[0] = ReadVertex(vreader);
data_index++;
start_vtx = 1;
}
data[0] = ReadVertex(vreader);
for (int vtx = 1; vtx < vertex_count; ++vtx) {
for (int vtx = start_vtx; vtx < vertex_count; ++vtx) {
if (indices) {
vreader.Goto(idxConv.convert(vtx) - index_lower_bound);
} else {
vreader.Goto(vtx);
}
data[2 - (vtx % 2)] = ReadVertex(vreader);
data[2 - ((data_index++) % 2)] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
@ -404,7 +431,7 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
} else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);

View file

@ -126,7 +126,7 @@ public:
static DrawingCoords ScreenToDrawing(const ScreenCoords& coords);
static ScreenCoords DrawingToScreen(const DrawingCoords& coords);
static void SubmitPrimitive(void* vertices, void* indices, u32 prim_type, int vertex_count, u32 vertex_type, int *bytesRead);
static void SubmitPrimitive(void* vertices, void* indices, GEPrimitiveType prim_type, int vertex_count, u32 vertex_type, int *bytesRead);
static bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
static VertexData ReadVertex(VertexReader& vreader);