implement handling for glitched polygon edge cases
This commit is contained in:
parent
a72b79a55a
commit
920cc6bb91
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
@ -1245,6 +1249,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;
|
||||
}
|
||||
|
||||
|
@ -1266,6 +1295,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)
|
||||
|
@ -1292,6 +1322,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;
|
||||
|
||||
|
@ -1303,7 +1337,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;
|
||||
|
@ -2039,6 +2073,7 @@ void GPU3D::ExecuteCommand() noexcept
|
|||
VertexNumInPoly = 0;
|
||||
NumConsecutivePolygons = 0;
|
||||
LastStripPolygon = NULL;
|
||||
UpdateLastPoly = false;
|
||||
CurPolygonAttr = PolygonAttr;
|
||||
break;
|
||||
|
||||
|
@ -2073,6 +2108,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
|
||||
|
|
15
src/GPU3D.h
15
src/GPU3D.h
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -272,6 +284,7 @@ public:
|
|||
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;
|
||||
|
||||
|
|
|
@ -595,7 +595,7 @@ void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y
|
|||
{
|
||||
Polygon* polygon = rp->PolyData;
|
||||
|
||||
while (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
||||
while ((y >= polygon->SlopePosition[rp->NextVL][1]) && rp->CurVL != polygon->VBottom)
|
||||
{
|
||||
rp->CurVL = rp->NextVL;
|
||||
|
||||
|
@ -613,8 +613,18 @@ void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y
|
|||
}
|
||||
}
|
||||
|
||||
// note: if the end or current position in a slope is above the start point
|
||||
// it seems to seek forwards(?) until the value overflows at 256
|
||||
// this can be emulated by just adding 256 to them
|
||||
if (y < polygon->Vertices[rp->CurVL]->FinalPosition[1])
|
||||
y += 256;
|
||||
|
||||
s32 y1 = polygon->Vertices[rp->NextVL]->FinalPosition[1];
|
||||
if (y1 < polygon->Vertices[rp->CurVL]->FinalPosition[1])
|
||||
y1 += 256;
|
||||
|
||||
rp->XL = rp->SlopeL.Setup(polygon->Vertices[rp->CurVL]->FinalPosition[0], polygon->Vertices[rp->NextVL]->FinalPosition[0],
|
||||
polygon->Vertices[rp->CurVL]->FinalPosition[1], polygon->Vertices[rp->NextVL]->FinalPosition[1],
|
||||
polygon->Vertices[rp->CurVL]->FinalPosition[1], y1,
|
||||
polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], y);
|
||||
}
|
||||
|
||||
|
@ -622,7 +632,7 @@ void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32
|
|||
{
|
||||
Polygon* polygon = rp->PolyData;
|
||||
|
||||
while (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
||||
while ((y >= polygon->SlopePosition[rp->NextVR][1]) && rp->CurVR != polygon->VBottom)
|
||||
{
|
||||
rp->CurVR = rp->NextVR;
|
||||
|
||||
|
@ -640,8 +650,18 @@ void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32
|
|||
}
|
||||
}
|
||||
|
||||
// note: if the end or current position in a slope is above the start point
|
||||
// it seems to seek forwards(?) until the value overflows at 256
|
||||
// this can be emulated by just adding 256 to them
|
||||
if (y < polygon->Vertices[rp->CurVR]->FinalPosition[1])
|
||||
y += 256;
|
||||
|
||||
s32 y1 = polygon->Vertices[rp->NextVR]->FinalPosition[1];
|
||||
if (y1 < polygon->Vertices[rp->CurVR]->FinalPosition[1])
|
||||
y1 += 256;
|
||||
|
||||
rp->XR = rp->SlopeR.Setup(polygon->Vertices[rp->CurVR]->FinalPosition[0], polygon->Vertices[rp->NextVR]->FinalPosition[0],
|
||||
polygon->Vertices[rp->CurVR]->FinalPosition[1], polygon->Vertices[rp->NextVR]->FinalPosition[1],
|
||||
polygon->Vertices[rp->CurVR]->FinalPosition[1], y1,
|
||||
polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], y);
|
||||
}
|
||||
|
||||
|
@ -678,18 +698,18 @@ void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* poly
|
|||
int i;
|
||||
|
||||
i = 1;
|
||||
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
|
||||
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
|
||||
if (polygon->SlopePosition[i][0] < polygon->SlopePosition[vtop][0]) vtop = i;
|
||||
if (polygon->SlopePosition[i][0] > polygon->SlopePosition[vbot][0]) vbot = i;
|
||||
|
||||
i = nverts - 1;
|
||||
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
|
||||
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
|
||||
if (polygon->SlopePosition[i][0] < polygon->SlopePosition[vtop][0]) vtop = i;
|
||||
if (polygon->SlopePosition[i][0] > polygon->SlopePosition[vbot][0]) vbot = i;
|
||||
|
||||
rp->CurVL = vtop; rp->NextVL = vtop;
|
||||
rp->CurVR = vbot; rp->NextVR = vbot;
|
||||
|
||||
rp->XL = rp->SlopeL.SetupDummy(polygon->Vertices[rp->CurVL]->FinalPosition[0]);
|
||||
rp->XR = rp->SlopeR.SetupDummy(polygon->Vertices[rp->CurVR]->FinalPosition[0]);
|
||||
rp->XL = rp->SlopeL.SetupDummy(polygon->SlopePosition[rp->CurVL][0]);
|
||||
rp->XR = rp->SlopeR.SetupDummy(polygon->SlopePosition[rp->CurVR][0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -723,12 +743,12 @@ void SoftRenderer::RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon*
|
|||
|
||||
if (polygon->YTop != polygon->YBottom)
|
||||
{
|
||||
if (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
||||
if ((y >= polygon->SlopePosition[rp->NextVL][1] || y == polygon->Vertices[rp->CurVL]->FinalPosition[1]) && rp->CurVL != polygon->VBottom)
|
||||
{
|
||||
SetupPolygonLeftEdge(rp, y);
|
||||
}
|
||||
|
||||
if (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
||||
if ((y >= polygon->SlopePosition[rp->NextVR][1] || y == polygon->Vertices[rp->CurVR]->FinalPosition[1]) && rp->CurVR != polygon->VBottom)
|
||||
{
|
||||
SetupPolygonRightEdge(rp, y);
|
||||
}
|
||||
|
@ -833,10 +853,10 @@ void SoftRenderer::RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon*
|
|||
else if (y == polygon->YBottom-1) yedge = 0x8;
|
||||
int edge;
|
||||
|
||||
if (xstart < 0) xstart = 0; // negative values are clamped to 0 before interpolation is determined
|
||||
s32 x = xstart;
|
||||
Interpolator<0> interpX(xstart, xend+1, wl, wr);
|
||||
|
||||
if (x < 0) x = 0;
|
||||
s32 xlimit;
|
||||
|
||||
// for shadow masks: set stencil bits where the depth test fails.
|
||||
|
@ -948,12 +968,12 @@ void SoftRenderer::RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s3
|
|||
|
||||
if (polygon->YTop != polygon->YBottom)
|
||||
{
|
||||
if (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
||||
if ((y >= polygon->SlopePosition[rp->NextVL][1] || y == polygon->Vertices[rp->CurVL]->FinalPosition[1]) && rp->CurVL != polygon->VBottom)
|
||||
{
|
||||
SetupPolygonLeftEdge(rp, y);
|
||||
}
|
||||
|
||||
if (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
||||
if ((y >= polygon->SlopePosition[rp->NextVR][1] || y == polygon->Vertices[rp->CurVR]->FinalPosition[1]) && rp->CurVR != polygon->VBottom)
|
||||
{
|
||||
SetupPolygonRightEdge(rp, y);
|
||||
}
|
||||
|
@ -1082,11 +1102,11 @@ void SoftRenderer::RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s3
|
|||
if (y == polygon->YTop) yedge = 0x4;
|
||||
else if (y == polygon->YBottom-1) yedge = 0x8;
|
||||
int edge;
|
||||
|
||||
|
||||
if (xstart < 0) xstart = 0; // negative values are clamped to 0 before interpolation is determined
|
||||
s32 x = xstart;
|
||||
Interpolator<0> interpX(xstart, xend+1, wl, wr);
|
||||
|
||||
if (x < 0) x = 0;
|
||||
s32 xlimit;
|
||||
|
||||
s32 xcov = 0;
|
||||
|
|
|
@ -129,6 +129,7 @@ private:
|
|||
constexpr void SetX(s32 x)
|
||||
{
|
||||
x -= x0;
|
||||
if (x > xdiff) x = xdiff; // may or may not be correct
|
||||
this->x = x;
|
||||
if (xdiff != 0 && !linear)
|
||||
{
|
||||
|
@ -284,7 +285,7 @@ private:
|
|||
// instead, 1/y is calculated and then multiplied by x
|
||||
// TODO: this is still not perfect (see for example x=169 y=33)
|
||||
if (ylen == 0)
|
||||
Increment = 0;
|
||||
Increment = xlen<<18; // this case should only be triggered by glitched polygons
|
||||
else if (ylen == xlen && xlen != 1)
|
||||
Increment = 0x40000;
|
||||
else
|
||||
|
@ -315,8 +316,6 @@ private:
|
|||
|
||||
dx += (y - y0) * Increment;
|
||||
|
||||
s32 x = XVal();
|
||||
|
||||
int interpoffset = (Increment >= 0x40000) && (side ^ Negative);
|
||||
Interp.Setup(y0-interpoffset, y1-interpoffset, w0, w1);
|
||||
Interp.SetX(y);
|
||||
|
@ -324,17 +323,16 @@ private:
|
|||
// used for calculating AA coverage
|
||||
if (XMajor) xcov_incr = (ylen << 10) / xlen;
|
||||
|
||||
return x;
|
||||
return XVal();
|
||||
}
|
||||
|
||||
constexpr s32 Step()
|
||||
{
|
||||
dx += Increment;
|
||||
dx = dx + Increment & 0xFFFFFFF; // seems to be a 28 bit integer
|
||||
y++;
|
||||
|
||||
s32 x = XVal();
|
||||
Interp.SetX(y);
|
||||
return x;
|
||||
return XVal();
|
||||
}
|
||||
|
||||
constexpr s32 XVal() const
|
||||
|
@ -343,8 +341,6 @@ private:
|
|||
if (Negative) ret = x0 - (dx >> 18);
|
||||
else ret = x0 + (dx >> 18);
|
||||
|
||||
if (ret < xmin) ret = xmin;
|
||||
else if (ret > xmax) ret = xmax;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue