softgpu: Clip only on -Z, cull if entirely outside.

This is important for several issues, like #12058 or #12060, where
something is drawn entirely outside valid Z, and should be culled.
This commit is contained in:
Unknown W. Brackets committed 2021-09-09 20:13:42 -07:00
1 parent 0b73c1ce83
commit 121c56e6db
1 file changed
+65 -38
+65 -38
View File
@@ -22,6 +22,7 @@
#include "GPU/Software/Clipper.h"
#include "GPU/Software/Rasterizer.h"
#include "GPU/Software/RasterizerRectangle.h"
#include "GPU/Software/TransformUnit.h"
#include "Common/Profiler/Profiler.h"
@@ -29,11 +30,6 @@ namespace Clipper {
enum {
SKIP_FLAG = -1,
CLIP_POS_X_BIT = 0x01,
CLIP_NEG_X_BIT = 0x02,
CLIP_POS_Y_BIT = 0x04,
CLIP_NEG_Y_BIT = 0x08,
CLIP_POS_Z_BIT = 0x10,
CLIP_NEG_Z_BIT = 0x20,
};
@@ -41,11 +37,6 @@ static inline int CalcClipMask(const ClipCoords& v)
{
int mask = 0;
// This checks `x / w` compared to 1 or -1, skipping the division.
if (v.x > v.w) mask |= CLIP_POS_X_BIT;
if (v.x < -v.w) mask |= CLIP_NEG_X_BIT;
if (v.y > v.w) mask |= CLIP_POS_Y_BIT;
if (v.y < -v.w) mask |= CLIP_NEG_Y_BIT;
if (v.z > v.w) mask |= CLIP_POS_Z_BIT;
if (v.z < -v.w) mask |= CLIP_NEG_Z_BIT;
return mask;
}
@@ -134,9 +125,37 @@ static void RotateUVThrough(const VertexData &tl, const VertexData &br, VertexDa
}
}
void ProcessTriangleInternal(VertexData &v0, VertexData &v1, VertexData &v2, const VertexData &provoking, bool fromRectangle);
static inline bool CheckOutsideZ(ClipCoords p, int &pos, int &neg) {
constexpr float outsideValue = 1.000030517578125f;
float z = p.z / p.w;
if (z >= outsideValue) {
pos++;
return true;
}
if (-z >= outsideValue) {
neg++;
return true;
}
return false;
}
void ProcessRect(const VertexData& v0, const VertexData& v1)
{
if (!gstate.isModeThrough()) {
// We may discard the entire rect based on depth values.
int outsidePos = 0, outsideNeg = 0;
CheckOutsideZ(v0.clippos, outsidePos, outsideNeg);
CheckOutsideZ(v1.clippos, outsidePos, outsideNeg);
// With depth clamp off, we discard the rectangle if even one vert is outside.
if (outsidePos + outsideNeg > 0 && !gstate.isDepthClampEnabled())
return;
// With it on, both must be outside in the same direction.
else if (outsidePos >= 2 || outsideNeg >= 2)
return;
VertexData buf[4];
buf[0].clippos = ClipCoords(v0.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w);
buf[0].texturecoords = v0.texturecoords;
@@ -171,10 +190,10 @@ void ProcessRect(const VertexData& v0, const VertexData& v1)
}
// Four triangles to do backfaces as well. Two of them will get backface culled.
ProcessTriangle(*topleft, *topright, *bottomright, buf[3]);
ProcessTriangle(*bottomright, *topright, *topleft, buf[3]);
ProcessTriangle(*bottomright, *bottomleft, *topleft, buf[3]);
ProcessTriangle(*topleft, *bottomleft, *bottomright, buf[3]);
ProcessTriangleInternal(*topleft, *topright, *bottomright, buf[3], true);
ProcessTriangleInternal(*bottomright, *topright, *topleft, buf[3], true);
ProcessTriangleInternal(*bottomright, *bottomleft, *topleft, buf[3], true);
ProcessTriangleInternal(*topleft, *bottomleft, *bottomright, buf[3], true);
} else {
// through mode handling
@@ -245,23 +264,23 @@ void ProcessLine(VertexData& v0, VertexData& v1)
return;
}
int outsidePos = 0, outsideNeg = 0;
CheckOutsideZ(v0.clippos, outsidePos, outsideNeg);
CheckOutsideZ(v1.clippos, outsidePos, outsideNeg);
// With depth clamp off, we discard the line if even one vert is outside.
if (outsidePos + outsideNeg > 0 && !gstate.isDepthClampEnabled())
return;
// With it on, both must be outside in the same direction.
else if (outsidePos >= 2 || outsideNeg >= 2)
return;
VertexData *Vertices[2] = {&v0, &v1};
int mask0 = CalcClipMask(v0.clippos);
int mask1 = CalcClipMask(v1.clippos);
int mask = mask0 | mask1;
if (mask0 & mask1) {
// Even if clipping is disabled, we can discard if the line is entirely outside.
return;
}
if (mask) {
CLIP_LINE(CLIP_POS_X_BIT, -1, 0, 0, 1);
CLIP_LINE(CLIP_NEG_X_BIT, 1, 0, 0, 1);
CLIP_LINE(CLIP_POS_Y_BIT, 0, -1, 0, 1);
CLIP_LINE(CLIP_NEG_Y_BIT, 0, 1, 0, 1);
CLIP_LINE(CLIP_POS_Z_BIT, 0, 0, 0, 1);
CLIP_LINE(CLIP_NEG_Z_BIT, 0, 0, 1, 1);
}
@@ -271,7 +290,7 @@ void ProcessLine(VertexData& v0, VertexData& v1)
Rasterizer::DrawLine(data[0], data[1]);
}
void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const VertexData &provoking) {
void ProcessTriangleInternal(VertexData &v0, VertexData &v1, VertexData &v2, const VertexData &provoking, bool fromRectangle) {
if (gstate.isModeThrough()) {
// In case of cull reordering, make sure the right color is on the final vertex.
if (gstate.getShadeMode() == GE_SHADE_FLAT) {
@@ -307,7 +326,20 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const Verte
mask |= CalcClipMask(v1.clippos);
mask |= CalcClipMask(v2.clippos);
if (mask) {
if (mask && !fromRectangle) {
// We may discard the entire triangle based on depth values. First check what's outside.
int outsidePos = 0, outsideNeg = 0;
for (int i = 0; i < 3; ++i) {
CheckOutsideZ(Vertices[i]->clippos, outsidePos, outsideNeg);
}
// With depth clamp off, we discard the triangle if even one vert is outside.
if (outsidePos + outsideNeg > 0 && !gstate.isDepthClampEnabled())
return;
// With it on, all three must be outside in the same direction.
else if (outsidePos >= 3 || outsideNeg >= 3)
return;
for (int i = 0; i < 3; i += 3) {
int vlist[2][2*6+1];
int *inlist = vlist[0], *outlist = vlist[1];
@@ -323,13 +355,8 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const Verte
indices[1] = SKIP_FLAG;
indices[2] = SKIP_FLAG;
// The PSP doesn't clip on the sides (so, commented out) but it does appear to have a Z clipper.
// POLY_CLIP(CLIP_POS_X_BIT, -1, 0, 0, 1);
// POLY_CLIP(CLIP_NEG_X_BIT, 1, 0, 0, 1);
// POLY_CLIP(CLIP_POS_Y_BIT, 0, -1, 0, 1);
// POLY_CLIP(CLIP_NEG_Y_BIT, 0, 1, 0, 1);
POLY_CLIP(CLIP_POS_Z_BIT, 0, 0, 0, 1);
POLY_CLIP(CLIP_NEG_Z_BIT, 0, 0, 1, 1);
// The PSP only clips on negative Z (importantly, regardless of viewport.)
POLY_CLIP(CLIP_NEG_Z_BIT, 0, 0, 1, 1);
// transform the poly in inlist into triangles
indices[0] = inlist[0];
@@ -341,10 +368,6 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const Verte
indices[numIndices++] = inlist[j];
}
}
} else if (CalcClipMask(v0.clippos) & CalcClipMask(v1.clippos) & CalcClipMask(v2.clippos)) {
// If clipping is disabled, only discard the current primitive
// if all three vertices lie outside one of the clipping planes
return;
}
for (int i = 0; i + 3 <= numIndices; i += 3) {
@@ -365,4 +388,8 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const Verte
}
}
void ProcessTriangle(VertexData &v0, VertexData &v1, VertexData &v2, const VertexData &provoking) {
ProcessTriangleInternal(v0, v1, v2, provoking, false);
}
} // namespace