Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

softgpu: Fix over-optimization of alpha test #17214

Merged
merged 3 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions GPU/Software/RasterizerRectangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ template <GEBufferFormat fmt, bool alphaBlend>
static inline void DrawSinglePixel(u16 *pixel, const u32 color_in) {
u32 new_color;
// Because of this check, we only support src.a / 1-src.a blending.
// We take advantage of short circuiting by checking the constant (template) value first.
if (!alphaBlend || (color_in >> 24) == 255) {
new_color = color_in & 0xFFFFFF;
} else {
Expand Down Expand Up @@ -142,7 +141,7 @@ template <bool alphaBlend>
static inline void DrawSinglePixel32(u32 *pixel, const u32 color_in) {
u32 new_color;
// Because of this check, we only support src.a / 1-src.a blending.
if ((color_in >> 24) == 255 || !alphaBlend) {
if (!alphaBlend || (color_in >> 24) == 255) {
new_color = color_in & 0xFFFFFF;
} else {
const u32 old_color = *pixel;
Expand Down Expand Up @@ -231,7 +230,7 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I
return ToVec4IntResult(out);
}

template <GEBufferFormat fmt, bool isWhite, bool alphaBlend>
template <GEBufferFormat fmt, bool isWhite, bool alphaBlend, bool alphaTestZero>
static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
const u8 *texptr = state.texptr[0];
uint16_t texbufw = state.texbufw[0];
Expand All @@ -245,7 +244,7 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1,
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> tex_color = fetchFunc(s, t, texptr, texbufw, 0, state.samplerID);
if (isWhite) {
if (!alphaBlend || tex_color.a() != 0) {
if (!alphaTestZero || tex_color.a() != 0) {
u32 tex_color32 = tex_color.ToRGBA();
if (fmt == GE_FORMAT_8888)
DrawSinglePixel32<alphaBlend>(pixel32, tex_color32);
Expand All @@ -255,7 +254,7 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1,
} else {
Vec4<int> prim_color = c0;
prim_color = Vec4<int>(ModulateRGBA(ToVec4IntArg(prim_color), ToVec4IntArg(tex_color), state.samplerID));
if (!alphaBlend || prim_color.a() > 0) {
if (!alphaTestZero || prim_color.a() > 0) {
if (fmt == GE_FORMAT_8888)
DrawSinglePixel32<alphaBlend>(pixel32, prim_color.ToRGBA());
else
Expand All @@ -272,27 +271,38 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1,
}
}

template <bool isWhite, bool alphaBlend>
template <bool isWhite, bool alphaBlend, bool alphaTestZero>
static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
switch (state.pixelID.FBFormat()) {
case GE_FORMAT_565:
DrawSpriteTex<GE_FORMAT_565, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
DrawSpriteTex<GE_FORMAT_565, isWhite, alphaBlend, alphaTestZero>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_5551:
DrawSpriteTex<GE_FORMAT_5551, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
DrawSpriteTex<GE_FORMAT_5551, isWhite, alphaBlend, alphaTestZero>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_4444:
DrawSpriteTex<GE_FORMAT_4444, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
DrawSpriteTex<GE_FORMAT_4444, isWhite, alphaBlend, alphaTestZero>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_8888:
DrawSpriteTex<GE_FORMAT_8888, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
DrawSpriteTex<GE_FORMAT_8888, isWhite, alphaBlend, alphaTestZero>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
default:
// Invalid, don't draw anything...
break;
}
}

template <bool isWhite>
static inline void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
// Standard alpha blending implies skipping alpha zero.
if (state.pixelID.alphaBlend)
DrawSpriteTex<isWhite, true, true>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
else if (state.pixelID.AlphaTestFunc() != GE_COMP_ALWAYS)
DrawSpriteTex<isWhite, false, true>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
else
DrawSpriteTex<isWhite, false, false>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
}

template <GEBufferFormat fmt, bool alphaBlend>
static void DrawSpriteNoTex(const DrawingCoords &pos0, const DrawingCoords &pos1, u32 color0, const RasterizerState &state) {
if (alphaBlend && Vec4<int>::FromRGBA(color0).a() == 0)
Expand Down Expand Up @@ -393,15 +403,9 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran

if (UseDrawSinglePixel(pixelID) && (samplerID.TexFunc() == GE_TEXFUNC_MODULATE || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) && samplerID.useTextureAlpha) {
if (isWhite || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) {
if (pixelID.alphaBlend)
DrawSpriteTex<true, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
else
DrawSpriteTex<true, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
} else {
if (pixelID.alphaBlend)
DrawSpriteTex<false, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
else
DrawSpriteTex<false, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
}
} else {
float dsf = ds * (1.0f / (float)(1 << state.samplerID.width0Shift));
Expand Down
12 changes: 9 additions & 3 deletions headless/HeadlessHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,20 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) {
ScreenshotComparer comparer(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT);
double errors = comparer.Compare(comparisonScreenshot_);
if (errors < 0)
SendDebugOutput(comparer.GetError() + "\n");
SendAndCollectOutput(comparer.GetError() + "\n");

if (errors > maxScreenshotError_)
SendDebugOutput(StringFromFormat("Screenshot MSE: %f\n", errors));
SendAndCollectOutput(StringFromFormat("Screenshot MSE: %f\n", errors));

if (errors > maxScreenshotError_ && writeFailureScreenshot_) {
if (comparer.SaveActualBitmap(Path("__testfailure.bmp")))
SendDebugOutput("Actual output written to: __testfailure.bmp\n");
SendAndCollectOutput("Actual output written to: __testfailure.bmp\n");
comparer.SaveVisualComparisonPNG(Path("__testcompare.png"));
}
}

void HeadlessHost::SendAndCollectOutput(const std::string &output) {
SendDebugOutput(output);
if (PSP_CoreParameter().collectDebugOutput)
*PSP_CoreParameter().collectDebugOutput += output;
}
2 changes: 2 additions & 0 deletions headless/HeadlessHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class HeadlessHost {
virtual void SwapBuffers() {}

protected:
void SendAndCollectOutput(const std::string &output);

Path comparisonScreenshot_;
double maxScreenshotError_ = 0.0;
std::string debugOutputBuffer_;
Expand Down