Skip to content

Commit

Permalink
SoftGPU: Implement CONTINUE prim type.
Browse files Browse the repository at this point in the history
Matches tests (except 3D lines, which aren't implemented yet.)
  • Loading branch information
unknownbrackets committed Apr 30, 2017
1 parent ee68f09 commit 1666586
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 33 deletions.
3 changes: 2 additions & 1 deletion GPU/Software/SoftGpu.cpp
Expand Up @@ -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);
Expand Down
89 changes: 58 additions & 31 deletions GPU/Software/TransformUnit.cpp
Expand Up @@ -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;
Expand All @@ -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.

Expand All @@ -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);
}
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices) {
vreader.Goto(idxConv.convert(vtx) - index_lower_bound);
} else {
vreader.Goto(vtx);
}

data[i] = ReadVertex(vreader);
if (outside_range_flag)
break;
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;
}
Expand All @@ -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;
}

Expand All @@ -307,15 +322,17 @@ 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);
} else {
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;
Expand All @@ -326,15 +343,17 @@ 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;
}

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) {
Expand All @@ -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;
Expand All @@ -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]);
Expand All @@ -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;
Expand All @@ -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]);
Expand Down
2 changes: 1 addition & 1 deletion GPU/Software/TransformUnit.h
Expand Up @@ -126,7 +126,7 @@ class TransformUnit
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);
Expand Down

0 comments on commit 1666586

Please sign in to comment.