Skip to content

Commit

Permalink
Merge pull request #16241 from unknownbrackets/softgpu-rect
Browse files Browse the repository at this point in the history
softgpu: Correct linear interp for uneven positions
  • Loading branch information
hrydgard authored Oct 17, 2022
2 parents 799a533 + 7eb7bd5 commit 123311b
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 36 deletions.
30 changes: 22 additions & 8 deletions GPU/Software/Rasterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -796,11 +796,18 @@ void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &
int entireY1 = std::min(v0.screenpos.y, v1.screenpos.y);
int entireX2 = std::max(v0.screenpos.x, v1.screenpos.x) - 1;
int entireY2 = std::max(v0.screenpos.y, v1.screenpos.y) - 1;
int minX = std::max(entireX1, range.x1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minY = std::max(entireY1, range.y1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minX = std::max(entireX1 & ~(SCREEN_SCALE_FACTOR - 1), range.x1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minY = std::max(entireY1 & ~(SCREEN_SCALE_FACTOR - 1), range.y1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int maxX = std::min(entireX2, range.x2);
int maxY = std::min(entireY2, range.y2);

// If TL x or y was after the half, we don't draw the pixel.
// TODO: Verify what center is used, allowing slight offset makes gpu/primitives/trianglefan pass.
if (minX < entireX1 - 1)
minX += SCREEN_SCALE_FACTOR;
if (minY < entireY1 - 1)
minY += SCREEN_SCALE_FACTOR;

RasterizerState state = OptimizeFlatRasterizerState(rastState, v1);

Vec2f rowST(0.0f, 0.0f);
Expand All @@ -819,8 +826,8 @@ void DrawRectangle(const VertexData &v0, const VertexData &v1, const BinCoords &
tc1.t() *= 1.0f / (float)(1 << state.samplerID.height0Shift);
}

int diffX = (entireX2 - entireX1 + 1) / SCREEN_SCALE_FACTOR;
int diffY = (entireY2 - entireY1 + 1) / SCREEN_SCALE_FACTOR;
float diffX = (entireX2 - entireX1 + 1) / (float)SCREEN_SCALE_FACTOR;
float diffY = (entireY2 - entireY1 + 1) / (float)SCREEN_SCALE_FACTOR;
float diffS = tc1.s() - tc0.s();
float diffT = tc1.t() - tc0.t();

Expand Down Expand Up @@ -853,8 +860,8 @@ 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);
rowST += (sty / (float)(SCREEN_SCALE_FACTOR * 2)) * (minY - entireY1);
rowST += (stx / (float)(SCREEN_SCALE_FACTOR * 2)) * (minX - entireX1 + 1);
rowST += (sty / (float)(SCREEN_SCALE_FACTOR * 2)) * (minY - entireY1 + 1);
}

// And now what we add to spread out to 4 values.
Expand Down Expand Up @@ -1043,10 +1050,17 @@ void ClearRectangle(const VertexData &v0, const VertexData &v1, const BinCoords
int entireY1 = std::min(v0.screenpos.y, v1.screenpos.y);
int entireX2 = std::max(v0.screenpos.x, v1.screenpos.x) - 1;
int entireY2 = std::max(v0.screenpos.y, v1.screenpos.y) - 1;
int minX = std::max(entireX1, range.x1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minY = std::max(entireY1, range.y1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minX = std::max(entireX1 & ~(SCREEN_SCALE_FACTOR - 1), range.x1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int minY = std::max(entireY1 & ~(SCREEN_SCALE_FACTOR - 1), range.y1) | (SCREEN_SCALE_FACTOR / 2 - 1);
int maxX = std::min(entireX2, range.x2);
int maxY = std::min(entireY2, range.y2);

// If TL x or y was after the half, we don't draw the pixel.
if (minX < entireX1 - 1)
minX += SCREEN_SCALE_FACTOR;
if (minY < entireY1 - 1)
minY += SCREEN_SCALE_FACTOR;

const DrawingCoords pprime = TransformUnit::ScreenToDrawing(minX, minY);
const DrawingCoords pend = TransformUnit::ScreenToDrawing(maxX, maxY);
auto &pixelID = state.pixelID;
Expand Down
63 changes: 37 additions & 26 deletions GPU/Software/RasterizerRectangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,40 +483,51 @@ bool DetectRectangleFromStrip(const RasterizerState &state, const ClipVertexData
return false;
}

bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int c, int *tlIndex, int *brIndex) {
bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int *tlIndex, int *brIndex) {
// Color and Z must be flat.
for (int i = 1; i < c; ++i) {
int tl = 0, br = 0;
for (int i = 1; i < 4; ++i) {
if (!AreCoordsRectangleCompatible(state, data[i], data[0]))
return false;

if (data[i].v.screenpos.x <= data[tl].v.screenpos.x && data[i].v.screenpos.y <= data[tl].v.screenpos.y)
tl = i;
if (data[i].v.screenpos.x >= data[br].v.screenpos.x && data[i].v.screenpos.y >= data[br].v.screenpos.y)
br = i;
}

// Check for the common case: a single TL-TR-BR-BL.
if (c == 4) {
const auto &pos0 = data[0].v.screenpos, &pos1 = data[1].v.screenpos;
const auto &pos2 = data[2].v.screenpos, &pos3 = data[3].v.screenpos;
if (pos0.x == pos3.x && pos1.x == pos2.x && pos0.y == pos1.y && pos3.y == pos2.y) {
// Looking like yes. Set TL/BR based on y order first...
*tlIndex = pos0.y > pos3.y ? 2 : 0;
*brIndex = pos0.y > pos3.y ? 0 : 2;
// And if it's horizontally flipped, trade to the actual TL/BR.
if (pos0.x > pos1.x) {
*tlIndex ^= 1;
*brIndex ^= 1;
}
*tlIndex = tl;
*brIndex = br;

// Do we need to think about rotation?
if (!state.enableTextures)
return true;
int tr = 1, bl = 1;
for (int i = 0; i < 4; ++i) {
if (i == tl || i == br)
continue;

const auto &textl = data[*tlIndex].v.texturecoords, &textr = data[*tlIndex ^ 1].v.texturecoords;
const auto &texbl = data[*brIndex ^ 1].v.texturecoords, &texbr = data[*brIndex].v.texturecoords;
if (data[i].v.screenpos.x <= data[tl].v.screenpos.x && data[i].v.screenpos.y >= data[tl].v.screenpos.y)
bl = i;
if (data[i].v.screenpos.x >= data[br].v.screenpos.x && data[i].v.screenpos.y <= data[br].v.screenpos.y)
tr = i;
}

if (textl.x == texbl.x && textr.x == texbr.x && textl.y == textr.y && texbl.y == texbr.y) {
// Okay, the texture is also good, but let's avoid rotation issues.
const auto &postl = data[*tlIndex].v.screenpos;
const auto &posbr = data[*brIndex].v.screenpos;
return textl.y < texbr.y && postl.y < posbr.y && textl.x < texbr.x && postl.x < posbr.x;
}
// Must have found each of the coordinates.
if (tl + tr + bl + br != 6)
return false;

// Note the common case is a single TL-TR-BR-BL.
const auto &postl = data[tl].v.screenpos, &postr = data[tr].v.screenpos;
const auto &posbr = data[br].v.screenpos, &posbl = data[bl].v.screenpos;
if (postl.x == posbl.x && postr.x == posbr.x && postl.y == postr.y && posbl.y == posbr.y) {
// Do we need to think about rotation?
if (!state.enableTextures)
return true;

const auto &textl = data[tl].v.texturecoords, &textr = data[tr].v.texturecoords;
const auto &texbl = data[bl].v.texturecoords, &texbr = data[br].v.texturecoords;

if (textl.x == texbl.x && textr.x == texbr.x && textl.y == textr.y && texbl.y == texbr.y) {
// Okay, the texture is also good, but let's avoid rotation issues.
return textl.y < texbr.y && postl.y < posbr.y && textl.x < texbr.x && postl.x < posbr.x;
}
}

Expand Down
2 changes: 1 addition & 1 deletion GPU/Software/RasterizerRectangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Rasterizer {
void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state);

bool DetectRectangleFromStrip(const RasterizerState &state, const ClipVertexData data[4], int *tlIndex, int *brIndex);
bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int c, int *tlIndex, int *brIndex);
bool DetectRectangleFromFan(const RasterizerState &state, const ClipVertexData *data, int *tlIndex, int *brIndex);
bool DetectRectangleFromPair(const RasterizerState &state, const ClipVertexData data[6], int *tlIndex, int *brIndex);
bool DetectRectangleThroughModeSlices(const RasterizerState &state, const ClipVertexData data[4]);
}
2 changes: 1 addition & 1 deletion GPU/Software/TransformUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ void TransformUnit::SubmitPrimitive(const void* vertices, const void* indices, G
}

int tl = -1, br = -1;
if (Rasterizer::DetectRectangleFromFan(binner_->State(), data_, vertex_count, &tl, &br)) {
if (Rasterizer::DetectRectangleFromFan(binner_->State(), data_, &tl, &br)) {
Clipper::ProcessRect(data_[tl], data_[br], *binner_);
break;
}
Expand Down

0 comments on commit 123311b

Please sign in to comment.