Skip to content

Commit

Permalink
softgpu: Clip only on -Z, cull if entirely outside.
Browse files Browse the repository at this point in the history
This is important for several issues, like hrydgard#12058 or hrydgard#12060, where
something is drawn entirely outside valid Z, and should be culled.
  • Loading branch information
unknownbrackets committed Sep 10, 2021
1 parent 0b73c1c commit 121c56e
Showing 1 changed file with 65 additions and 38 deletions.
103 changes: 65 additions & 38 deletions GPU/Software/Clipper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,21 @@
#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"

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,
};

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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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);
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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];
Expand All @@ -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];
Expand All @@ -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) {
Expand All @@ -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

0 comments on commit 121c56e

Please sign in to comment.