diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 44561dfa..4746a7fa 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -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 diff --git a/src/GPU3D.h b/src/GPU3D.h index f5446f34..f5705c1f 100644 --- a/src/GPU3D.h +++ b/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; diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index a8da14cd..3203e10a 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -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; diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 45b2c539..6c3833f4 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -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; }