softgpu: Prepare dedicated rectangle path.

We're still sometimes using the slow rect-as-triangles path, let's do
something faster.  As a first step, just handle binning.
This commit is contained in:
Unknown W. Brackets 2022-02-08 23:11:12 -08:00
parent bc71a0d8e2
commit cc6491342e
5 changed files with 90 additions and 97 deletions

View File

@ -74,6 +74,10 @@ static inline void DrawBinItem(const BinItem &item, const RasterizerState &state
ClearRectangle(item.v0, item.v1, item.range, state);
break;
case BinItemType::RECT:
DrawRectangle(item.v0, item.v1, item.range, state);
break;
case BinItemType::SPRITE:
DrawSprite(item.v0, item.v1, item.range, state);
break;
@ -316,6 +320,17 @@ void BinManager::AddClearRect(const VertexData &v0, const VertexData &v1) {
Expand(range);
}
void BinManager::AddRect(const VertexData &v0, const VertexData &v1) {
const BinCoords range = Range(v0, v1);
if (range.Invalid())
return;
if (queue_.Full())
Drain();
queue_.Push(BinItem{ BinItemType::RECT, stateIndex_, range, v0, v1 });
Expand(range);
}
void BinManager::AddSprite(const VertexData &v0, const VertexData &v1) {
const BinCoords range = Range(v0, v1);
if (range.Invalid())

View File

@ -27,6 +27,7 @@ class DrawBinItemsTask;
enum class BinItemType {
TRIANGLE,
CLEAR_RECT,
RECT,
SPRITE,
LINE,
POINT,
@ -189,6 +190,7 @@ public:
void AddTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2);
void AddClearRect(const VertexData &v0, const VertexData &v1);
void AddRect(const VertexData &v0, const VertexData &v1);
void AddSprite(const VertexData &v0, const VertexData &v1);
void AddLine(const VertexData &v0, const VertexData &v1);
void AddPoint(const VertexData &v0);

View File

@ -117,17 +117,6 @@ inline float clip_dotprod(const VertexData &vert, float A, float B, float C, flo
} \
}
static void RotateUV(const VertexData &tl, const VertexData &br, VertexData &tr, VertexData &bl) {
const int x1 = tl.screenpos.x;
const int x2 = br.screenpos.x;
const int y1 = tl.screenpos.y;
const int y2 = br.screenpos.y;
if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2)) {
std::swap(bl.texturecoords, tr.texturecoords);
}
}
static inline bool CheckOutsideZ(ClipCoords p, int &pos, int &neg) {
constexpr float outsideValue = 1.000030517578125f;
float z = p.z / p.w;
@ -156,102 +145,32 @@ void ProcessRect(const VertexData &v0, const VertexData &v1, BinManager &binner)
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].screenpos = TransformUnit::ClipToScreen(buf[0].clippos);
buf[0].texturecoords = v0.texturecoords;
if (v0.fogdepth != v1.fogdepth) {
// Rectangles seem to always use nearest along X for fog depth, but reversed.
// TODO: Check exactness of middle.
VertexData vhalf0 = v1;
vhalf0.screenpos.x = v0.screenpos.x + (v1.screenpos.x - v0.screenpos.x) / 2;
buf[1].clippos = ClipCoords(v0.clippos.x, v1.clippos.y, v1.clippos.z, v1.clippos.w);
buf[1].screenpos = TransformUnit::ClipToScreen(buf[1].clippos);
buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y);
VertexData vhalf1 = v1;
vhalf1.screenpos.x = v0.screenpos.x + (v1.screenpos.x - v0.screenpos.x) / 2;
vhalf1.screenpos.y = v0.screenpos.y;
buf[2].clippos = ClipCoords(v1.clippos.x, v0.clippos.y, v1.clippos.z, v1.clippos.w);
buf[2].screenpos = TransformUnit::ClipToScreen(buf[2].clippos);
buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y);
VertexData vrev1 = v1;
vrev1.fogdepth = v0.fogdepth;
buf[3] = v1;
// Color and depth values of second vertex are used for the whole rectangle
buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0;
buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1;
buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth;
VertexData* topleft = &buf[0];
VertexData* topright = &buf[1];
VertexData* bottomleft = &buf[2];
VertexData* bottomright = &buf[3];
for (int i = 0; i < 4; ++i) {
if (buf[i].clippos.x < topleft->clippos.x && buf[i].clippos.y < topleft->clippos.y)
topleft = &buf[i];
if (buf[i].clippos.x > topright->clippos.x && buf[i].clippos.y < topright->clippos.y)
topright = &buf[i];
if (buf[i].clippos.x < bottomleft->clippos.x && buf[i].clippos.y > bottomleft->clippos.y)
bottomleft = &buf[i];
if (buf[i].clippos.x > bottomright->clippos.x && buf[i].clippos.y > bottomright->clippos.y)
bottomright = &buf[i];
binner.AddRect(v0, vhalf0);
binner.AddRect(vhalf1, vrev1);
} else {
binner.AddRect(v0, v1);
}
RotateUV(*topleft, *bottomright, *topright, *bottomleft);
// Four triangles to do backfaces as well. Two of them will get backface culled.
// We already clipped, so we don't need additional processing.
binner.AddTriangle(*topleft, *topright, *bottomright);
binner.AddTriangle(*bottomright, *topright, *topleft);
binner.AddTriangle(*bottomright, *bottomleft, *topleft);
binner.AddTriangle(*topleft, *bottomleft, *bottomright);
} else {
// through mode handling
if (Rasterizer::RectangleFastPath(v0, v1, binner)) {
return;
}
VertexData buf[4];
buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z);
buf[0].texturecoords = v0.texturecoords;
buf[1].screenpos = ScreenCoords(v0.screenpos.x, v1.screenpos.y, v1.screenpos.z);
buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y);
buf[2].screenpos = ScreenCoords(v1.screenpos.x, v0.screenpos.y, v1.screenpos.z);
buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y);
buf[3] = v1;
// Color and depth values of second vertex are used for the whole rectangle
buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0;
buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1; // is color1 ever used in through mode?
buf[0].clippos.w = buf[1].clippos.w = buf[2].clippos.w = buf[3].clippos.w = 1.0f;
buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth = 1.0f;
VertexData* topleft = &buf[0];
VertexData* topright = &buf[1];
VertexData* bottomleft = &buf[2];
VertexData* bottomright = &buf[3];
// DrawTriangle always culls, so sort out the drawing order.
for (int i = 0; i < 4; ++i) {
if (buf[i].screenpos.x < topleft->screenpos.x && buf[i].screenpos.y < topleft->screenpos.y)
topleft = &buf[i];
if (buf[i].screenpos.x > topright->screenpos.x && buf[i].screenpos.y < topright->screenpos.y)
topright = &buf[i];
if (buf[i].screenpos.x < bottomleft->screenpos.x && buf[i].screenpos.y > bottomleft->screenpos.y)
bottomleft = &buf[i];
if (buf[i].screenpos.x > bottomright->screenpos.x && buf[i].screenpos.y > bottomright->screenpos.y)
bottomright = &buf[i];
}
RotateUV(v0, v1, *topright, *bottomleft);
if (gstate.isModeClear() && !gstate.isDitherEnabled()) {
} else if (gstate.isModeClear() && !gstate.isDitherEnabled()) {
binner.AddClearRect(v0, v1);
} else {
// Four triangles to do backfaces as well. Two of them will get backface culled.
binner.AddTriangle(*topleft, *topright, *bottomleft);
binner.AddTriangle(*bottomleft, *topright, *topleft);
binner.AddTriangle(*topright, *bottomright, *bottomleft);
binner.AddTriangle(*bottomleft, *bottomright, *topright);
binner.AddRect(v0, v1);
}
}
}

View File

@ -888,6 +888,62 @@ void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &
drawSlice(v0, v1, v2, range.x1, range.y1, range.x2, range.y2, state);
}
static void RotateUV(const VertexData &tl, const VertexData &br, VertexData &tr, VertexData &bl) {
const int x1 = tl.screenpos.x;
const int x2 = br.screenpos.x;
const int y1 = tl.screenpos.y;
const int y2 = br.screenpos.y;
if ((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2)) {
std::swap(bl.texturecoords, tr.texturecoords);
}
}
void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) {
VertexData buf[4];
buf[0].screenpos = ScreenCoords(v0.screenpos.x, v0.screenpos.y, v1.screenpos.z);
buf[0].texturecoords = v0.texturecoords;
buf[1].screenpos = ScreenCoords(v0.screenpos.x, v1.screenpos.y, v1.screenpos.z);
buf[1].texturecoords = Vec2<float>(v0.texturecoords.x, v1.texturecoords.y);
buf[2].screenpos = ScreenCoords(v1.screenpos.x, v0.screenpos.y, v1.screenpos.z);
buf[2].texturecoords = Vec2<float>(v1.texturecoords.x, v0.texturecoords.y);
buf[3] = v1;
// Color and depth values of second vertex are used for the whole rectangle
buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0;
buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1;
buf[0].clippos.w = buf[1].clippos.w = buf[2].clippos.w = buf[3].clippos.w;
// TODO
buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth;
VertexData *topleft = &buf[0];
VertexData *topright = &buf[1];
VertexData *bottomleft = &buf[2];
VertexData *bottomright = &buf[3];
// DrawTriangle always culls, so sort out the drawing order.
for (int i = 0; i < 4; ++i) {
if (buf[i].screenpos.x < topleft->screenpos.x && buf[i].screenpos.y < topleft->screenpos.y)
topleft = &buf[i];
if (buf[i].screenpos.x > topright->screenpos.x && buf[i].screenpos.y < topright->screenpos.y)
topright = &buf[i];
if (buf[i].screenpos.x < bottomleft->screenpos.x && buf[i].screenpos.y > bottomleft->screenpos.y)
bottomleft = &buf[i];
if (buf[i].screenpos.x > bottomright->screenpos.x && buf[i].screenpos.y > bottomright->screenpos.y)
bottomright = &buf[i];
}
RotateUV(v0, v1, *topright, *bottomleft);
DrawTriangle(*topleft, *topright, *bottomleft, range, state);
DrawTriangle(*bottomleft, *topright, *topleft, range, state);
DrawTriangle(*topright, *bottomright, *bottomleft, range, state);
DrawTriangle(*bottomleft, *bottomright, *topright, range, state);
}
void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerState &state) {
ScreenCoords pos = v0.screenpos;
Vec4<int> prim_color = v0.color0;

View File

@ -71,6 +71,7 @@ void ComputeRasterizerState(RasterizerState *state);
// Draws a triangle if its vertices are specified in counter-clockwise order
void DrawTriangle(const VertexData &v0, const VertexData &v1, const VertexData &v2, const BinCoords &range, const RasterizerState &state);
void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state);
void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerState &state);
void DrawLine(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state);
void ClearRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state);