softgpu: Simplify vertex range culling.
The previous logic was harder to understand and easier to get wrong. Just drop them when clipping the primitive.
This commit is contained in:
parent
028a341cc8
commit
de080e2594
4 changed files with 47 additions and 69 deletions
|
@ -133,6 +133,10 @@ static inline bool CheckOutsideZ(ClipCoords p, int &pos, int &neg) {
|
|||
|
||||
void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner) {
|
||||
if (!binner.State().throughMode) {
|
||||
// If any verts were outside range, throw the entire prim away.
|
||||
if (v0.OutsideRange() || v1.OutsideRange())
|
||||
return;
|
||||
|
||||
// We may discard the entire rect based on depth values.
|
||||
int outsidePos = 0, outsideNeg = 0;
|
||||
CheckOutsideZ(v0.clippos, outsidePos, outsideNeg);
|
||||
|
@ -176,6 +180,12 @@ void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner)
|
|||
}
|
||||
|
||||
void ProcessPoint(const VertexData &v0, BinManager &binner) {
|
||||
// If any verts were outside range, throw the entire prim away.
|
||||
if (!binner.State().throughMode) {
|
||||
if (v0.OutsideRange())
|
||||
return;
|
||||
}
|
||||
|
||||
// Points need no clipping. Will be bounds checked in the rasterizer (which seems backwards?)
|
||||
binner.AddPoint(v0);
|
||||
}
|
||||
|
@ -187,6 +197,10 @@ void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner)
|
|||
return;
|
||||
}
|
||||
|
||||
// If any verts were outside range, throw the entire prim away.
|
||||
if (v0.OutsideRange() || v1.OutsideRange())
|
||||
return;
|
||||
|
||||
int outsidePos = 0, outsideNeg = 0;
|
||||
CheckOutsideZ(v0.clippos, outsidePos, outsideNeg);
|
||||
CheckOutsideZ(v1.clippos, outsidePos, outsideNeg);
|
||||
|
@ -222,6 +236,10 @@ void ProcessLine(const VertexData &v0, const VertexData &v1, BinManager &binner)
|
|||
void ProcessTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const VertexData &provoking, BinManager &binner) {
|
||||
int mask = 0;
|
||||
if (!binner.State().throughMode) {
|
||||
// If any verts were outside range, throw the entire prim away.
|
||||
if (v0.OutsideRange() || v1.OutsideRange() || v2.OutsideRange())
|
||||
return;
|
||||
|
||||
mask |= CalcClipMask(v0.clippos);
|
||||
mask |= CalcClipMask(v1.clippos);
|
||||
mask |= CalcClipMask(v2.clippos);
|
||||
|
|
|
@ -355,15 +355,18 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b
|
|||
}
|
||||
|
||||
static bool AreCoordsRectangleCompatible(const RasterizerState &state, const VertexData &data0, const VertexData &data1) {
|
||||
if (!(data1.color0 == data0.color0))
|
||||
if (data1.color0 != data0.color0)
|
||||
return false;
|
||||
if (!(data1.screenpos.z == data0.screenpos.z)) {
|
||||
if (data1.screenpos.z != data0.screenpos.z) {
|
||||
// Sometimes, we don't actually care about z.
|
||||
if (state.pixelID.depthWrite || state.pixelID.DepthTestFunc() != GE_COMP_ALWAYS)
|
||||
return false;
|
||||
}
|
||||
if (!state.throughMode) {
|
||||
if (!state.throughMode && !(data1.color1 == data0.color1))
|
||||
if (data1.color1 != data0.color1)
|
||||
return false;
|
||||
// This means it should be culled, outside range.
|
||||
if (data1.OutsideRange() || data0.OutsideRange())
|
||||
return false;
|
||||
// Do we have to think about perspective correction or slope mip level?
|
||||
if (state.enableTextures && data1.clippos.w != data0.clippos.w) {
|
||||
|
|
|
@ -273,7 +273,7 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) {
|
|||
state->roundToScreen = &ClipToScreenInternal<false, true>;
|
||||
}
|
||||
|
||||
VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState &state, bool &outside_range_flag) {
|
||||
VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState &state) {
|
||||
PROFILE_THIS_SCOPE("read_vert");
|
||||
VertexData vertex;
|
||||
|
||||
|
@ -362,9 +362,13 @@ VertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformState
|
|||
#else
|
||||
screenScaled = vertex.clippos.xyz() * state.screenScale / vertex.clippos.w + state.screenAdd;
|
||||
#endif
|
||||
bool outside_range_flag = false;
|
||||
vertex.screenpos = state.roundToScreen(screenScaled, vertex.clippos, &outside_range_flag);
|
||||
if (outside_range_flag)
|
||||
if (outside_range_flag) {
|
||||
// We use this, essentially, as the flag.
|
||||
vertex.screenpos.x = 0x7FFFFFFF;
|
||||
return vertex;
|
||||
}
|
||||
|
||||
if (state.enableFog) {
|
||||
vertex.fogdepth = (viewpos.z + state.fogEnd) * state.fogSlope;
|
||||
|
@ -495,9 +499,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
bool skipCull = !gstate.isCullEnabled() || gstate.isModeClear();
|
||||
const CullType cullType = skipCull ? CullType::OFF : (gstate.getCullMode() ? CullType::CCW : CullType::CW);
|
||||
|
||||
bool outside_range_flag = false;
|
||||
|
||||
if (vreader.isThrough() && cullType == CullType::OFF && prim_type == GE_PRIM_TRIANGLES && data_index_ + vertex_count >= 6 && ((data_index_ + vertex_count) % 6) == 0) {
|
||||
if (vreader.isThrough() && cullType == CullType::OFF && prim_type == GE_PRIM_TRIANGLES && data_index_ == 0 && vertex_count >= 6 && ((vertex_count) % 6) == 0) {
|
||||
// Some games send rectangles as a series of regular triangles.
|
||||
// We look for this, but only in throughmode.
|
||||
VertexData buf[6];
|
||||
|
@ -513,14 +515,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
vreader.Goto(vtx);
|
||||
}
|
||||
|
||||
buf[buf_index++] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
if (buf_index >= 3 && outside_range_flag) {
|
||||
// Cull, just pretend it didn't happen.
|
||||
buf_index -= 3;
|
||||
outside_range_flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[buf_index++] = ReadVertex(vreader, transformState);
|
||||
if (buf_index < 6)
|
||||
continue;
|
||||
|
||||
|
@ -565,19 +560,13 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
vreader.Goto(vtx);
|
||||
}
|
||||
|
||||
data_[data_index_++] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
data_[data_index_++] = ReadVertex(vreader, transformState);
|
||||
if (data_index_ < vtcs_per_prim) {
|
||||
// Keep reading. Note: an incomplete prim will stay read for GE_PRIM_KEEP_PREVIOUS.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Okay, we've got enough verts. Reset the index for next time.
|
||||
data_index_ = 0;
|
||||
if (outside_range_flag) {
|
||||
// Cull the prim if it was outside, and move to the next prim.
|
||||
outside_range_flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (prim_type) {
|
||||
case GE_PRIM_TRIANGLES:
|
||||
|
@ -607,19 +596,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
vreader.Goto(vtx);
|
||||
}
|
||||
|
||||
data_[data_index_++] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
if (outside_range_flag) {
|
||||
outside_range_flag = false;
|
||||
// Note: this is the post increment index. If odd, we set the first vert.
|
||||
if (data_index_ & 1) {
|
||||
// Skip the next one and forget this one.
|
||||
vtx++;
|
||||
data_index_--;
|
||||
} else {
|
||||
// Forget both of the last 2.
|
||||
data_index_ -= 2;
|
||||
}
|
||||
}
|
||||
data_[data_index_++] = ReadVertex(vreader, transformState);
|
||||
|
||||
if (data_index_ == 4 && vreader.isThrough() && cullType == CullType::OFF) {
|
||||
if (Rasterizer::DetectRectangleThroughModeSlices(binner_->State(), data_)) {
|
||||
|
@ -653,13 +630,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
vreader.Goto(vtx);
|
||||
}
|
||||
|
||||
data_[(data_index_++) & 1] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
if (outside_range_flag) {
|
||||
// Drop all primitives containing the current vertex
|
||||
skip_count = 2;
|
||||
outside_range_flag = false;
|
||||
continue;
|
||||
}
|
||||
data_[(data_index_++) & 1] = ReadVertex(vreader, transformState);
|
||||
|
||||
if (skip_count) {
|
||||
--skip_count;
|
||||
|
@ -687,12 +658,12 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
} else {
|
||||
vreader.Goto(base + vtx);
|
||||
}
|
||||
data_[vtx] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
data_[vtx] = ReadVertex(vreader, transformState);
|
||||
}
|
||||
|
||||
// If a strip is effectively a rectangle, draw it as such!
|
||||
int tl = -1, br = -1;
|
||||
if (!outside_range_flag && Rasterizer::DetectRectangleFromStrip(binner_->State(), data_, &tl, &br)) {
|
||||
if (Rasterizer::DetectRectangleFromStrip(binner_->State(), data_, &tl, &br)) {
|
||||
Clipper::ProcessRect(data_[tl], data_[br], *binner_);
|
||||
start_vtx += 2;
|
||||
if (base + 4 >= vertex_count) {
|
||||
|
@ -711,7 +682,6 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
}
|
||||
}
|
||||
|
||||
outside_range_flag = false;
|
||||
for (int vtx = start_vtx; vtx < vertex_count; ++vtx) {
|
||||
if (indices) {
|
||||
vreader.Goto(ConvertIndex(vtx) - index_lower_bound);
|
||||
|
@ -720,13 +690,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
}
|
||||
|
||||
int provoking_index = (data_index_++) % 3;
|
||||
data_[provoking_index] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
if (outside_range_flag) {
|
||||
// Drop all primitives containing the current vertex
|
||||
skip_count = 2;
|
||||
outside_range_flag = false;
|
||||
continue;
|
||||
}
|
||||
data_[provoking_index] = ReadVertex(vreader, transformState);
|
||||
|
||||
if (skip_count) {
|
||||
--skip_count;
|
||||
|
@ -754,13 +718,9 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
} else {
|
||||
vreader.Goto(0);
|
||||
}
|
||||
data_[0] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
data_[0] = ReadVertex(vreader, transformState);
|
||||
data_index_++;
|
||||
start_vtx = 1;
|
||||
|
||||
// If the central vertex is outside range, all the points are toast.
|
||||
if (outside_range_flag)
|
||||
break;
|
||||
}
|
||||
|
||||
if (data_index_ == 1 && vertex_count == 4 && cullType == CullType::OFF) {
|
||||
|
@ -770,17 +730,16 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
} else {
|
||||
vreader.Goto(vtx);
|
||||
}
|
||||
data_[vtx] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
data_[vtx] = ReadVertex(vreader, transformState);
|
||||
}
|
||||
|
||||
int tl = -1, br = -1;
|
||||
if (!outside_range_flag && Rasterizer::DetectRectangleFromFan(binner_->State(), data_, vertex_count, &tl, &br)) {
|
||||
if (Rasterizer::DetectRectangleFromFan(binner_->State(), data_, vertex_count, &tl, &br)) {
|
||||
Clipper::ProcessRect(data_[tl], data_[br], *binner_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outside_range_flag = false;
|
||||
for (int vtx = start_vtx; vtx < vertex_count; ++vtx) {
|
||||
if (indices) {
|
||||
vreader.Goto(ConvertIndex(vtx) - index_lower_bound);
|
||||
|
@ -789,13 +748,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
|
|||
}
|
||||
|
||||
int provoking_index = 2 - ((data_index_++) % 2);
|
||||
data_[provoking_index] = ReadVertex(vreader, transformState, outside_range_flag);
|
||||
if (outside_range_flag) {
|
||||
// Drop all primitives containing the current vertex
|
||||
skip_count = 2;
|
||||
outside_range_flag = false;
|
||||
continue;
|
||||
}
|
||||
data_[provoking_index] = ReadVertex(vreader, transformState);
|
||||
|
||||
if (skip_count) {
|
||||
--skip_count;
|
||||
|
|
|
@ -90,6 +90,10 @@ struct VertexData {
|
|||
color1 = LerpInt<Vec3<int>, 256>(Vec3<int>::FromRGB(a.color1), Vec3<int>::FromRGB(b.color1), t_int).ToRGB();
|
||||
}
|
||||
|
||||
bool OutsideRange() const {
|
||||
return screenpos.x == 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
ClipCoords clippos;
|
||||
Vec2<float> texturecoords;
|
||||
uint32_t color0;
|
||||
|
@ -138,7 +142,7 @@ public:
|
|||
SoftDirty GetDirty();
|
||||
|
||||
private:
|
||||
VertexData ReadVertex(VertexReader &vreader, const TransformState &lstate, bool &outside_range_flag);
|
||||
VertexData ReadVertex(VertexReader &vreader, const TransformState &state);
|
||||
void SendTriangle(CullType cullType, const VertexData *verts, int provoking = 2);
|
||||
|
||||
u8 *decoded_ = nullptr;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue