Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes for some glitches with polygon strips #2060

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
63 changes: 59 additions & 4 deletions src/GPU3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ void GPU3D::DoSavestate(Savestate* file) noexcept

file->Var32(&poly->NumVertices);

file->VarArray(poly->SlopePosition, sizeof(s32)*10*2);

file->VarArray(poly->FinalZ, sizeof(s32)*10);
file->VarArray(poly->FinalW, sizeof(s32)*10);
file->Bool32(&poly->WBuffer);
Expand Down Expand Up @@ -487,7 +489,7 @@ void GPU3D::DoSavestate(Savestate* file) noexcept
poly->Degenerate = true;
}

if (poly->YBottom > 192) poly->Degenerate = true;
if (poly->YBottom > 192 && !poly->Translucent) poly->Degenerate = true;
}
}

Expand Down Expand Up @@ -1098,8 +1100,10 @@ void GPU3D::SubmitPolygon() noexcept
}

// compute screen coordinates

for (int i = clipstart; i < nverts; i++)
// hardware does this pass for shared vertices in polygon strips, even though it was already done for them last polygon
// however it doesn't recalculate all of the previous polygon's internal info (used for determining how to rasterize it)
// despite potentially changing their coordinates if a viewport change occured mid-strip...
for (int i = (UpdateLastPoly ? 0 : clipstart); i < nverts; i++)
{
Vertex* vtx = &clippedvertices[i];

Expand Down Expand Up @@ -1212,6 +1216,7 @@ void GPU3D::SubmitPolygon() noexcept

poly->Degenerate = false;
poly->Type = 0;
poly->OOBRendering = UpdateLastPoly;

poly->FacingView = facingview;

Expand All @@ -1232,6 +1237,19 @@ void GPU3D::SubmitPolygon() noexcept
{
poly->Vertices[0] = reusedvertices[0];
poly->Vertices[1] = reusedvertices[1];

// null vertices poly invalidation:
// 1. Start a polygon strip
// 2. Submit at least one polygon
// 3. Swap buffers
// 4. Don't send a begin command
// 4. submit a new polygon (1 vertex for tri, 2 for quad)
// 5. if the new polygon reuses vertices, it will be "degenerate" due to them being null pointers (theory)
if (NullVertices)
{
poly->Degenerate = true;
NullVertices -= (PolygonMode - 1); // subt. 1 if tri strip, subt. 2 if quad strip.
}
}
else
{
Expand All @@ -1245,6 +1263,31 @@ void GPU3D::SubmitPolygon() noexcept
NumVertices += 2;
}

// if a viewport command was submitted mid-polygon strip the "true" coords and sort order of a vertex in the last polygon can be changed retroactively
if (UpdateLastPoly)
{
// update final coords and sortkey to match new vertex coordinates
// yes, *only* those values... this causes the polygon to be rasterized in an extremely glitchy manner
poly->Vertices[0]->FinalPosition[0] = clippedvertices[0].FinalPosition[0];
poly->Vertices[0]->FinalPosition[1] = clippedvertices[0].FinalPosition[1];
poly->Vertices[1]->FinalPosition[0] = clippedvertices[1].FinalPosition[0];
poly->Vertices[1]->FinalPosition[1] = clippedvertices[1].FinalPosition[1];

s32 ytop = 192, ybot = 0;
Vertex** lastpolyvtx = LastStripPolygon->Vertices;
for (int i = 0; i < LastStripPolygon->NumVertices; i++)
{
if (lastpolyvtx[i]->FinalPosition[1] < ytop)
ytop = lastpolyvtx[i]->FinalPosition[1];
if (lastpolyvtx[i]->FinalPosition[1] > ybot)
ybot = lastpolyvtx[i]->FinalPosition[1];
}
LastStripPolygon->SortKey = (ybot << 8) | ytop;
if (LastStripPolygon->Translucent) LastStripPolygon->SortKey |= 0x10000;

// clear update flag
UpdateLastPoly = false;
}
poly->NumVertices += 2;
}

Expand All @@ -1266,6 +1309,7 @@ void GPU3D::SubmitPolygon() noexcept
}

// determine bounds of the polygon
// including where slopes begin and end
// also determine the W shift and normalize W
// normalization works both ways
// (ie two W's that span 12 bits or less will be brought to 16 bits)
Expand All @@ -1292,6 +1336,10 @@ void GPU3D::SubmitPolygon() noexcept
vbot = i;
}

// these values are used to determine where to begin/end slopes
poly->SlopePosition[i][0] = vtx->FinalPosition[0];
poly->SlopePosition[i][1] = vtx->FinalPosition[1];

u32 w = (u32)vtx->Position[3];
if (w == 0) poly->Degenerate = true;

Expand All @@ -1303,7 +1351,7 @@ void GPU3D::SubmitPolygon() noexcept
poly->YTop = ytop; poly->YBottom = ybot;
poly->XTop = xtop; poly->XBottom = xbot;

if (ybot > 192) poly->Degenerate = true;
if (ybot > 192 && !poly->Translucent) poly->Degenerate = true;

poly->SortKey = (ybot << 8) | ytop;
if (poly->Translucent) poly->SortKey |= 0x10000;
Expand Down Expand Up @@ -2039,7 +2087,9 @@ void GPU3D::ExecuteCommand() noexcept
VertexNumInPoly = 0;
NumConsecutivePolygons = 0;
LastStripPolygon = NULL;
UpdateLastPoly = false;
CurPolygonAttr = PolygonAttr;
NullVertices = 0;
break;

case 0x41: // end polygons
Expand All @@ -2062,6 +2112,8 @@ void GPU3D::ExecuteCommand() noexcept
PolygonPipeline = 0;
VertexSlotCounter = 0;
VertexSlotsFree = 1;
// previous polygon's vertices will be counted as "null" if a buffer swap occurs
if (PolygonMode >= 2) NullVertices = 2;
break;

case 0x60: // viewport x1,y1,x2,y2
Expand All @@ -2073,6 +2125,9 @@ void GPU3D::ExecuteCommand() noexcept
Viewport[3] = (191 - (entry.Param >> 24)) & 0xFF; // y1
Viewport[4] = (Viewport[2] - Viewport[0] + 1) & 0x1FF; // width
Viewport[5] = (Viewport[1] - Viewport[3] + 1) & 0xFF; // height

// set a flag that tells the next polygon to emulate a bug with polygon strips
if (LastStripPolygon) UpdateLastPoly = true;
break;

case 0x72: // vec test
Expand Down
17 changes: 16 additions & 1 deletion src/GPU3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ struct Vertex

// final vertex attributes.
// allows them to be reused in polygon strips.


// with sw renderer FinalPosition is primarily used for calculating the slope of a polygon (not where it begins/ends)
// (it does get used to determine where slopes should start and end with the gl renderers)
// the initial set of coordinates gets updated by the next polygon in a strip
// which can cause rendering issues if they wind up different than their initial value (due to a viewport change)
s32 FinalPosition[2];

s32 FinalColor[3];

// hi-res position (4-bit fractional part)
Expand All @@ -55,6 +60,13 @@ struct Polygon
Vertex* Vertices[10];
u32 NumVertices;

// essentially a per-polygon copy of its vertices' coordinates
// (not 100% sure why they do it like this? but a glitch requires this for proper behavior, so we gotta do it too)
// unlike each vertices' final position variable, it is *not* updated by the next polygon in a polygon strip
// it is used by the software renderer to determine where to begin/end each slope
// TODO: track hires versions of this for the hardware renderers to use?
s32 SlopePosition[10][2];

s32 FinalZ[10];
s32 FinalW[10];
bool WBuffer;
Expand All @@ -72,6 +84,7 @@ struct Polygon
bool IsShadow;

int Type; // 0=regular 1=line
bool OOBRendering;

u32 VTop, VBottom; // vertex indices
s32 YTop, YBottom; // Y coords
Expand Down Expand Up @@ -272,6 +285,7 @@ class GPU3D
u32 RenderClearAttr2 = 0;

bool RenderFrameIdentical = false; // not part of the hardware state, don't serialize
bool UpdateLastPoly = false; // used to track whether the next polygon should update the previous one's vtx coordinates (as a small optimization)

bool AbortFrame = false;

Expand Down Expand Up @@ -309,6 +323,7 @@ class GPU3D
u32 VertexNumInPoly = 0;
u32 NumConsecutivePolygons = 0;
Polygon* LastStripPolygon = nullptr;
u32 NullVertices = 0;
u32 NumOpaquePolygons = 0;

Vertex VertexRAM[6144 * 2] {};
Expand Down
Loading
Loading