mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-16 07:50:28 +02:00
Depth raster: Stop a row once it has left the triangle
The triangle is convex, so once a row has been entered and left, the rest of it is outside. This cut cycles by 5% in Wipeout Pure, where depth raster is on for the lens flares. The test checks the covered pixels against a per-pixel edge test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
83639a73e8
commit
0edfd93a3a
7 files changed
+137
-2
No files matched your search
@@ -1387,6 +1387,7 @@ if(UNITTEST)
|
||||
unittest/TestIRPassSimplify.cpp
|
||||
unittest/TestX64Emitter.cpp
|
||||
unittest/TestBoundingBox.cpp
|
||||
unittest/TestDepthRaster.cpp
|
||||
unittest/TestVertexJit.cpp
|
||||
unittest/TestVertexJit.h
|
||||
unittest/TestVFS.cpp
|
||||
|
||||
@@ -208,7 +208,7 @@ void DepthRaster4Triangles(int stats[3], uint16_t *depthBuf, int stride, DepthSc
|
||||
Vec4S32 oneStepY20 = Vec4S32::Splat(stepY20[t]);
|
||||
Vec4S32 oneStepX01 = Vec4S32::Splat(stepX01[t]);
|
||||
Vec4S32 oneStepY01 = Vec4S32::Splat(stepY01[t]);
|
||||
// Rasterize
|
||||
// Rasterize. The triangle is convex, so once a row has been entered and left, the rest of it is outside.
|
||||
for (int y = minYT; y <= maxYT; y += stepYSize, w0_row += oneStepY12, w1_row += oneStepY20, w2_row += oneStepY01, zrow += zdeltaY) {
|
||||
// Barycentric coordinates at start of row
|
||||
Vec4S32 w0 = w0_row;
|
||||
@@ -218,15 +218,18 @@ void DepthRaster4Triangles(int stats[3], uint16_t *depthBuf, int stride, DepthSc
|
||||
|
||||
uint16_t *rowPtr = depthBuf + stride * y;
|
||||
|
||||
bool rowEntered = false;
|
||||
for (int x = minXT; x <= maxXT; x += stepXSize, w0 += oneStepX12, w1 += oneStepX20, w2 += oneStepX01, zs += zdeltaX) {
|
||||
// If p is on or inside all edges for any pixels,
|
||||
// render those pixels.
|
||||
Vec4S32 signCalc = w0 | w1 | w2;
|
||||
|
||||
// TODO: Check if this check is profitable. Maybe only for big triangles?
|
||||
if (!AnyZeroSignBit(signCalc)) {
|
||||
if (rowEntered)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
rowEntered = true;
|
||||
|
||||
Vec4U16 bufferValues = Vec4U16::Load(rowPtr + x);
|
||||
Vec4U16 shortMaskInv = SignBits32ToMaskU16(signCalc);
|
||||
|
||||
@@ -1101,6 +1101,7 @@ ifeq ($(UNITTEST),1)
|
||||
$(SRC)/unittest/TestShaderGenerators.cpp \
|
||||
$(SRC)/unittest/TestThreadManager.cpp \
|
||||
$(SRC)/unittest/TestBoundingBox.cpp \
|
||||
$(SRC)/unittest/TestDepthRaster.cpp \
|
||||
$(SRC)/unittest/TestVertexJit.cpp \
|
||||
$(SRC)/unittest/TestTextureReplacer.cpp \
|
||||
$(SRC)/unittest/TestVFS.cpp \
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "GPU/Common/DepthRaster.h"
|
||||
#include "unittest/UnitTest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct RasterRng {
|
||||
u32 state = 0x9E3779B9;
|
||||
u32 Next() {
|
||||
state ^= state << 13;
|
||||
state ^= state >> 17;
|
||||
state ^= state << 5;
|
||||
return state;
|
||||
}
|
||||
int Range(int lo, int hi) { return lo + (int)(Next() % (u32)(hi - lo + 1)); }
|
||||
};
|
||||
|
||||
constexpr int BUF_W = 512;
|
||||
constexpr int BUF_H = 280;
|
||||
|
||||
// Which pixels DepthRaster4Triangles covers, one pixel at a time: inside (or on) all three edges, within the
|
||||
// triangle's bounds clipped to the scissor, with X widened to whole groups of four.
|
||||
void ReferenceCoverage(std::vector<u8> &covered, int x0, int y0, int x1, int y1, int x2, int y2, const DepthScissor &scissor) {
|
||||
const int minX = std::max(std::min({ x0, x1, x2 }), (int)scissor.x1);
|
||||
const int maxX = std::min(std::max({ x0, x1, x2 }), (int)scissor.x2);
|
||||
const int minY = std::max(std::min({ y0, y1, y2 }), (int)scissor.y1);
|
||||
const int maxY = std::min(std::max({ y0, y1, y2 }), (int)scissor.y2);
|
||||
if (maxX <= minX || maxY <= minY)
|
||||
return;
|
||||
if ((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0) < 10)
|
||||
return;
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int x = minX & ~3; x <= (maxX & ~3) + 3; x++) {
|
||||
const int w0 = (y1 - y2) * x + (x2 - x1) * y + (x1 * y2 - y1 * x2);
|
||||
const int w1 = (y2 - y0) * x + (x0 - x2) * y + (x2 * y0 - y2 * x0);
|
||||
const int w2 = (y0 - y1) * x + (x1 - x0) * y + (x0 * y1 - y0 * x1);
|
||||
if ((w0 | w1 | w2) >= 0)
|
||||
covered[y * BUF_W + x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// The depth rasterizer covers exactly the pixels a per-pixel edge test does. All triangles are at one depth, so
|
||||
// with a cleared buffer and Greater, the written pixels are the covered ones. (Low quality is left out: it
|
||||
// copies each row's blocks to the next row, along with what was there.)
|
||||
bool TestDepthRaster() {
|
||||
RasterRng rng;
|
||||
std::vector<u16> depth(BUF_W * BUF_H);
|
||||
std::vector<u8> covered(BUF_W * BUF_H);
|
||||
int mismatches = 0;
|
||||
int pixels = 0;
|
||||
|
||||
for (int trial = 0; trial < 4000; trial++) {
|
||||
DepthScissor scissor;
|
||||
scissor.x1 = (u16)rng.Range(0, 100);
|
||||
scissor.y1 = (u16)rng.Range(0, 60);
|
||||
scissor.x2 = (u16)rng.Range(380, 480);
|
||||
scissor.y2 = (u16)rng.Range(200, 270);
|
||||
DepthDraw draw{};
|
||||
draw.prim = GE_PRIM_TRIANGLES;
|
||||
draw.compareMode = ZCompareMode::Greater;
|
||||
|
||||
// Groups of four triangles: x, y and z of vertex 0 of each, then vertex 1, then vertex 2.
|
||||
const int groups = 1 + rng.Range(0, 3);
|
||||
alignas(16) int tx[48];
|
||||
alignas(16) int ty[48];
|
||||
alignas(16) float tz[48];
|
||||
std::fill(depth.begin(), depth.end(), 0);
|
||||
std::fill(covered.begin(), covered.end(), 0);
|
||||
// Small and thin triangles as well as big ones, some partly off the scissor.
|
||||
const int size = trial % 3 == 0 ? 8 : trial % 3 == 1 ? 60 : 400;
|
||||
for (int g = 0; g < groups; g++) {
|
||||
for (int t = 0; t < 4; t++) {
|
||||
const int cx = rng.Range(-40, 520);
|
||||
const int cy = rng.Range(-40, 300);
|
||||
int x[3], y[3];
|
||||
for (int v = 0; v < 3; v++) {
|
||||
x[v] = cx + rng.Range(-size, size);
|
||||
y[v] = cy + rng.Range(-size, size);
|
||||
tx[g * 12 + v * 4 + t] = x[v];
|
||||
ty[g * 12 + v * 4 + t] = y[v];
|
||||
tz[g * 12 + v * 4 + t] = 1000.0f;
|
||||
}
|
||||
ReferenceCoverage(covered, x[0], y[0], x[1], y[1], x[2], y[2], scissor);
|
||||
}
|
||||
}
|
||||
DepthRasterScreenVerts(depth.data(), BUF_W, tx, ty, tz, groups * 12, draw, scissor, false);
|
||||
|
||||
for (int i = 0; i < BUF_W * BUF_H; i++) {
|
||||
const bool written = depth[i] != 0;
|
||||
pixels += written;
|
||||
if (written != (covered[i] != 0) || (written && depth[i] != 1000)) {
|
||||
if (mismatches++ < 10)
|
||||
printf("Trial %d: pixel %d,%d is %d, expected %s\n", trial, i % BUF_W, i / BUF_W, depth[i], covered[i] ? "covered" : "empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("%d pixels covered\n", pixels);
|
||||
EXPECT_EQ_INT(mismatches, 0);
|
||||
EXPECT_TRUE(pixels > 1000000);
|
||||
return true;
|
||||
}
|
||||
@@ -3104,6 +3104,7 @@ bool TestMpegCsc();
|
||||
bool TestSplineTessellation();
|
||||
bool TestGEMath();
|
||||
bool TestBoundingBoxFast();
|
||||
bool TestDepthRaster();
|
||||
bool TestDemangle();
|
||||
|
||||
// The 8.3 short names games read out of d_private. These aren't verified against hardware yet (no
|
||||
@@ -3432,6 +3433,7 @@ TestItem availableTests[] = {
|
||||
#endif
|
||||
TEST_ITEM(VertexJit),
|
||||
TEST_ITEM(BoundingBoxFast),
|
||||
TEST_ITEM(DepthRaster),
|
||||
TEST_ITEM(VFPUSinCos),
|
||||
TEST_ITEM(VFPUDot),
|
||||
TEST_ITEM(MathUtil),
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
<ClCompile Include="TestThreadManager.cpp" />
|
||||
<ClCompile Include="TestTextureReplacer.cpp" />
|
||||
<ClCompile Include="TestBoundingBox.cpp" />
|
||||
<ClCompile Include="TestDepthRaster.cpp" />
|
||||
<ClCompile Include="TestVertexJit.cpp" />
|
||||
<ClCompile Include="TestVFS.cpp" />
|
||||
<ClCompile Include="TestZipSlip.cpp" />
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<ClCompile Include="TestArm64Emitter.cpp" />
|
||||
<ClCompile Include="TestArm64Disasm.cpp" />
|
||||
<ClCompile Include="TestBoundingBox.cpp" />
|
||||
<ClCompile Include="TestDepthRaster.cpp" />
|
||||
<ClCompile Include="TestVertexJit.cpp" />
|
||||
<ClCompile Include="..\ext\glew\glew.c" />
|
||||
<ClCompile Include="..\Windows\CaptureDevice.cpp">
|
||||
|
||||
Reference in new issue
Block a user