diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index a9d1950418..7a42ef5198 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -1293,8 +1293,11 @@ void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords & } // Okay, now move ST to the minX, minY position. - rowST += (stx / (float)(SCREEN_SCALE_FACTOR * 2)) * (minX - entireX1 + 1); - rowST += (sty / (float)(SCREEN_SCALE_FACTOR * 2)) * (minY - entireY1 + 1); + // This is the exact distance from the primitive's edge to the sample point - minX and minY + // already sit at the pixel center. The +1 that used to be here was compensating for + // TriangleEdge::Start's centerOff being a sixteenth of a pixel short of center. + rowST += (stx / (float)(SCREEN_SCALE_FACTOR * 2)) * (minX - entireX1); + rowST += (sty / (float)(SCREEN_SCALE_FACTOR * 2)) * (minY - entireY1); } // And now what we add to spread out to 4 values. diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index b86f625cdf..8c01ff4247 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -176,8 +176,11 @@ static ScreenCoords ClipToScreenInternal(Vec3f scaled, const ClipCoords &coords, // 16 = 0xFFFF / 4095.9375 // Round up at 0.625 to the nearest subpixel. static_assert(SCREEN_SCALE_FACTOR == 16, "Currently only supports scale 16"); - int x = (int)(scaled.x * 16.0f + 0.375f - gstate.getOffsetX16()); - int y = (int)(scaled.y * 16.0f + 0.375f - gstate.getOffsetY16()); + // floorf, not a plain (int) cast: the cast truncates toward zero, so once the region offset + // pushes the result negative it rounds the opposite way from the positive case - and from the + // through-mode path below, which is what made the two disagree by one subpixel. + int x = (int)floorf(scaled.x * 16.0f + 0.375f - gstate.getOffsetX16()); + int y = (int)floorf(scaled.y * 16.0f + 0.375f - gstate.getOffsetY16()); return ScreenCoords(x, y, scaled.z); } diff --git a/test.py b/test.py index 35f71dd724..47a6ba2eb6 100755 --- a/test.py +++ b/test.py @@ -167,6 +167,7 @@ tests_good = [ "gpu/displaylist/alignment", "gpu/dither/dither", "gpu/filtering/mipmaplinear", + "gpu/filtering/precisionnearest2d", "gpu/filtering/precisionnearest3d", "gpu/ge/break", "gpu/ge/context", @@ -424,12 +425,14 @@ tests_next = [ "gpu/displaylist/state", "gpu/filtering/linear", "gpu/filtering/nearest", + # These two pass with --graphics=software, but still fail on the hardware backends, + # so they stay here rather than in tests_good until those match too. "gpu/filtering/precisionlinear2d", "gpu/filtering/precisionlinear3d", - "gpu/filtering/precisionnearest2d", "gpu/ge/edramswizzle", "gpu/ge/get", "gpu/primitives/bezier", + # Passes with --graphics=software, still fails on the hardware backends. "gpu/primitives/continue", "gpu/primitives/immediate", "gpu/primitives/lines",