diff --git a/GPU/Software/DrawPixel.cpp b/GPU/Software/DrawPixel.cpp index d008d7d256..abd140f760 100644 --- a/GPU/Software/DrawPixel.cpp +++ b/GPU/Software/DrawPixel.cpp @@ -445,13 +445,14 @@ static inline Vec3 GetSourceFactor(PixelBlendFactor factor, const Vec4 return Vec3::AssignToAll(2 * source.a()); case PixelBlendFactor::DOUBLEINVSRCALPHA: - return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); + // Can be negative, see SignedBlendingResult(). + return Vec3::AssignToAll(255 - 2 * source.a()); case PixelBlendFactor::DOUBLEDSTALPHA: return Vec3::AssignToAll(2 * dst.a()); case PixelBlendFactor::DOUBLEINVDSTALPHA: - return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); + return Vec3::AssignToAll(255 - 2 * dst.a()); case PixelBlendFactor::FIX: default: @@ -502,13 +503,14 @@ static inline Vec3 GetDestFactor(PixelBlendFactor factor, const Vec4 & return Vec3::AssignToAll(2 * source.a()); case PixelBlendFactor::DOUBLEINVSRCALPHA: - return Vec3::AssignToAll(255 - std::min(2 * source.a(), 255)); + // Can be negative, see SignedBlendingResult(). + return Vec3::AssignToAll(255 - 2 * source.a()); case PixelBlendFactor::DOUBLEDSTALPHA: return Vec3::AssignToAll(2 * dst.a()); case PixelBlendFactor::DOUBLEINVDSTALPHA: - return Vec3::AssignToAll(255 - std::min(2 * dst.a(), 255)); + return Vec3::AssignToAll(255 - 2 * dst.a()); case PixelBlendFactor::FIX: default: @@ -524,10 +526,39 @@ static inline Vec3 GetDestFactor(PixelBlendFactor factor, const Vec4 & } // Removed inline here - it was never chosen to be inlined by the compiler anyway, too complex. +static bool IsSignedBlendFactor(PixelBlendFactor factor) { + return factor == PixelBlendFactor::DOUBLEINVSRCALPHA || factor == PixelBlendFactor::DOUBLEINVDSTALPHA; +} + +// 255 - 2a goes negative for a >= 128, and then the term subtracts: -(((2c + 1) * (2|f| + 1)) >> 10) +// (gpu/probe exp116, all factors and equations exact). +static Vec3 SignedBlendingResult(const PixelFuncID &pixelID, const Vec3 &srcfactor, const Vec3 &dstfactor, const Vec4 &source, const Vec4 &dst) { + auto term = [](int c, int f) { + const int t = ((2 * c + 1) * (2 * std::abs(f) + 1)) >> 10; + return f < 0 ? -t : t; + }; + Vec3 result; + for (int i = 0; i < 3; ++i) { + const int s = term(source[i], srcfactor[i]); + const int d = term(dst[i], dstfactor[i]); + switch (pixelID.AlphaBlendEq()) { + case GE_BLENDMODE_MUL_AND_SUBTRACT: result[i] = s - d; break; + case GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE: result[i] = d - s; break; + default: result[i] = s + d; break; + } + } + return result; +} + static Vec3 AlphaBlendingResult(const PixelFuncID &pixelID, const Vec4 &source, const Vec4 &dst) { - // Note: These factors cannot go below 0, but they can go above 255 when doubling. + // Note: These factors can go above 255 when doubling, and below 0 for the doubled inverses. Vec3 srcfactor = GetSourceFactor(pixelID.AlphaBlendSrc(), source, dst, pixelID.cached.alphaBlendSrc); Vec3 dstfactor = GetDestFactor(pixelID.AlphaBlendDst(), source, dst, pixelID.cached.alphaBlendDst); + const GEBlendMode eq = pixelID.AlphaBlendEq(); + if ((IsSignedBlendFactor(pixelID.AlphaBlendSrc()) || IsSignedBlendFactor(pixelID.AlphaBlendDst())) && + (eq == GE_BLENDMODE_MUL_AND_ADD || eq == GE_BLENDMODE_MUL_AND_SUBTRACT || eq == GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE)) { + return SignedBlendingResult(pixelID, srcfactor, dstfactor, source, dst); + } switch (pixelID.AlphaBlendEq()) { case GE_BLENDMODE_MUL_AND_ADD: @@ -769,6 +800,9 @@ void SOFTRAST_CALL DrawSinglePixel(int x, int y, int z, int fog, Vec4IntArg colo } SingleFunc GetSingleFunc(const PixelFuncID &id, BinManager *binner) { + // The jit clamps blend factors at 0. + if (id.alphaBlend && (IsSignedBlendFactor(id.AlphaBlendSrc()) || IsSignedBlendFactor(id.AlphaBlendDst()))) + return jitCache->GenericSingle(id); SingleFunc jitted = jitCache->GetSingle(id, binner); if (jitted) { return jitted;