From 4bff980d342a9119039d7442162e44c17fb248f6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 11 Jun 2019 15:45:20 -0700 Subject: [PATCH 01/14] interp: Use software dot for better accuracy. This gets some vdot/similar tests to give more accurate results compared to hardware. Also added flushing of zero and NaNs. Currently disabled, only enabled with a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 135 +++++++++++++++++++++++++++--------- Core/MIPS/MIPSVFPUUtils.cpp | 84 ++++++++++++++++++++++ Core/MIPS/MIPSVFPUUtils.h | 2 + 3 files changed, 188 insertions(+), 33 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index bb6c84abe5..9f1d923308 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -70,6 +70,8 @@ #define M_SQRT1_2 0.707106781186547524401f #endif +static const bool USE_VFPU_DOT = false; + union FloatBits { float f[4]; u32 u[4]; @@ -466,20 +468,33 @@ namespace MIPSInt for (int a = 0; a < n; a++) { for (int b = 0; b < n; b++) { - float sum = 0.0f; + union { float f; uint32_t u; } sum = { 0.0f }; if (a == n - 1 && b == n - 1) { // S and T prefixes work on the final (or maybe first, in reverse?) dot. ApplySwizzleS(&s[b * 4], V_Quad); ApplySwizzleT(&t[a * 4], V_Quad); - for (int c = 0; c < 4; c++) { - sum += s[b * 4 + c] * t[a * 4 + c]; + } + + if (USE_VFPU_DOT) { + sum.f = vfpu_dot(&s[b * 4], &t[a * 4]); + if (my_isnan(sum.f)) { + sum.u = 0x7f800001; + } else if ((sum.u & 0x7F800000) == 0) { + sum.u &= 0xFF800000; } } else { - for (int c = 0; c < n; c++) { - sum += s[b * 4 + c] * t[a * 4 + c]; + if (a == n - 1 && b == n - 1) { + for (int c = 0; c < 4; c++) { + sum.f += s[b * 4 + c] * t[a * 4 + c]; + } + } else { + for (int c = 0; c < n; c++) { + sum.f += s[b * 4 + c] * t[a * 4 + c]; + } } } - d[a * 4 + b] = sum; + + d[a * 4 + b] = sum.f; } } @@ -1123,7 +1138,7 @@ namespace MIPSInt void Int_VDot(MIPSOpcode op) { float s[4]{}, t[4]{}; - float d; + union { float f; uint32_t u; } d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -1132,12 +1147,23 @@ namespace MIPSInt ApplySwizzleS(s, V_Quad); ReadVector(t, sz, vt); ApplySwizzleT(t, V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + + if (USE_VFPU_DOT) { + d.f = vfpu_dot(s, t); + if (my_isnan(d.f)) { + d.u = 0x7f800001; + } else if ((d.u & 0x7F800000) == 0) { + d.u &= 0xFF800000; + } + } else { + d.f = 0.0f; + for (int i = 0; i < 4; i++) { + d.f += s[i] * t[i]; + } } - ApplyPrefixD(&d, V_Single); - WriteVector(&d, V_Single, vd); + + ApplyPrefixD(&d.f, V_Single); + WriteVector(&d.f, V_Single, vd); PC += 4; EatPrefixes(); } @@ -1565,7 +1591,8 @@ namespace MIPSInt } void Int_Vtfm(MIPSOpcode op) { - float s[16]{}, t[4]{}, d[4]; + float s[16]{}, t[4]{}; + FloatBits d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -1579,13 +1606,36 @@ namespace MIPSInt ReadMatrix(s, msz, vs); ReadVector(t, sz, vt); - for (int i = 0; i < ins; i++) { - d[i] = s[i * 4] * t[0]; - for (int k = 1; k < tn; k++) { - d[i] += s[i * 4 + k] * t[k]; + if (USE_VFPU_DOT) { + float t2[4]; + for (int i = 0; i < 4; i++) { + if (i < tn) { + t2[i] = t[i]; + } else if (i == ins) { + t2[i] = 1.0f; + } else { + t2[i] = 0.0f; + } } - if (ins >= n) { - d[i] += s[i * 4 + ins]; + + for (int i = 0; i < ins; i++) { + d.f[i] = vfpu_dot(&s[i * 4], t2); + + if (my_isnan(d.f[i])) { + d.u[i] = 0x7f800001; + } else if ((d.u[i] & 0x7F800000) == 0) { + d.u[i] &= 0xFF800000; + } + } + } else { + for (int i = 0; i < ins; i++) { + d.f[i] = s[i * 4] * t[0]; + for (int k = 1; k < tn; k++) { + d.f[i] += s[i * 4 + k] * t[k]; + } + if (ins >= n) { + d.f[i] += s[i * 4 + ins]; + } } } @@ -1610,17 +1660,27 @@ namespace MIPSInt ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); // Really this is the operation all rows probably use (with constant wiring.) - d[ins] = s[ins * 4] * t[0]; - for (int k = 1; k < 4; k++) { - d[ins] += s[ins * 4 + k] * t[k]; + if (USE_VFPU_DOT) { + d.f[ins] = vfpu_dot(&s[ins * 4], t); + + if (my_isnan(d.f[ins])) { + d.u[ins] = 0x7f800001; + } else if ((d.u[ins] & 0x7F800000) == 0) { + d.u[ins] &= 0xFF800000; + } + } else { + d.f[ins] = s[ins * 4] * t[0]; + for (int k = 1; k < 4; k++) { + d.f[ins] += s[ins * 4 + k] * t[k]; + } } // D prefix applies to the last element only. u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << ins; u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (ins + ins); currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; - ApplyPrefixD(d, sz); - WriteVector(d, sz, vd); + ApplyPrefixD(d.f, sz); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } @@ -1967,7 +2027,8 @@ namespace MIPSInt } void Int_VecDo3(MIPSOpcode op) { - float s[4], t[4], d[4]; + float s[4], t[4]; + FloatBits d; int vd = _VD; int vs = _VS; int vt = _VT; @@ -2011,10 +2072,18 @@ namespace MIPSInt for (int i = 0; i < n; i++) { switch (optype) { - case 0: d[i] = s[i] + t[i]; break; //vadd - case 1: d[i] = s[i] - t[i]; break; //vsub - case 7: d[i] = s[i] / t[i]; break; //vdiv - case 8: d[i] = s[i] * t[i]; break; //vmul + case 0: d.f[i] = s[i] + t[i]; break; //vadd + case 1: d.f[i] = s[i] - t[i]; break; //vsub + case 7: d.f[i] = s[i] / t[i]; break; //vdiv + case 8: d.f[i] = s[i] * t[i]; break; //vmul + } + + if (USE_VFPU_DOT) { + if (my_isnan(d.f[i])) { + d.u[i] = 0x7f800001; + } else if ((d.u[i] & 0x7F800000) == 0) { + d.u[i] &= 0xFF800000; + } } } @@ -2023,12 +2092,12 @@ namespace MIPSInt u32 lastmask = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & (1 << 8)) << (n - 1); u32 lastsat = (currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] & 3) << (n + n - 2); currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX] = lastmask | lastsat; - ApplyPrefixD(d, sz); + ApplyPrefixD(d.f, sz); } else { - RetainInvalidSwizzleST(d, sz); - ApplyPrefixD(d, sz); + RetainInvalidSwizzleST(d.f, sz); + ApplyPrefixD(d.f, sz); } - WriteVector(d, sz, vd); + WriteVector(d.f, sz, vd); PC += 4; EatPrefixes(); } diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 6c07bfef5a..1ad629d7c2 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -607,3 +607,87 @@ float Float16ToFloat32(unsigned short l) } return f; } + +static uint32_t get_uexp(uint32_t x) { + return (x >> 23) & 0xFF; +} + +static int32_t get_mant(uint32_t x) { + // Note: this returns the hidden 1. + return (x & 0x007FFFFF) | 0x00800000; +} + +static int32_t get_sign(uint32_t x) { + return x & 0x80000000; +} + +float vfpu_dot(float a[4], float b[4]) { + static const int EXTRA_BITS = 2; + union float2int { + uint32_t i; + float f; + } intermed[4]; + + for (int i = 0; i < 4; i++) { + intermed[i].f = a[i] * b[i]; + } + + uint32_t exps[4]; + int32_t mants[4]; + uint32_t max_exp = 0; + + for (int i = 0; i < 4; i++) { + exps[i] = get_uexp(intermed[i].i); + // Preserve extra bits of precision in the mantissa during the add. + mants[i] = get_mant(intermed[i].i) << EXTRA_BITS; + if (exps[i] > max_exp) { + max_exp = exps[i]; + } + } + + int32_t mant_sum = 0; + for (int i = 0; i < 4; i++) { + int exp = max_exp - exps[i]; + if (exp >= 32) { + mants[i] = 0; + } else { + mants[i] >>= max_exp - exps[i]; + } + if (get_sign(intermed[i].i)) { + mants[i] = -mants[i]; + } + mant_sum += mants[i]; + } + + uint32_t sign_sum = 0; + if (mant_sum < 0) { + sign_sum = 0x80000000; + mant_sum = -mant_sum; + } + // Chop off the extra bits. + mant_sum >>= EXTRA_BITS; + + if (mant_sum == 0 || max_exp == 0) { + return 0.0f; + } + + while (mant_sum < 0x00800000) { + mant_sum <<= 1; + max_exp -= 1; + } + while (mant_sum >= 0x01000000) { + mant_sum >>= 1; + max_exp += 1; + } + _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); + + if (max_exp >= 255) { + max_exp = 255; + mant_sum = 0; + } else if (max_exp == 0) { + return 0.0f; + } + + intermed[0].i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); + return intermed[0].f; +} diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 1489e33fc0..8e3d52bf70 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -97,6 +97,8 @@ inline float vfpu_clamp(float v, float min, float max) { return v >= max ? max : (v <= min ? min : v); } +float vfpu_dot(float a[4], float b[4]); + #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 #define VFPU_MASK_FLOAT16_SIGN 0x1 From 39017ea2008c42d69cc4a33722eb5bec927f8c38 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 12 Jun 2019 21:40:05 -0700 Subject: [PATCH 02/14] interp: Use vfpu_dot for vavg, vfad, vhdp. Disabled by default. --- Core/MIPS/MIPSIntVFPU.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 9f1d923308..e22e67fc5f 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1199,8 +1199,12 @@ namespace MIPSInt ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad); float sum = 0.0f; - for (int i = 0; i < 4; i++) { - sum += s[i] * t[i]; + if (USE_VFPU_DOT) { + sum = vfpu_dot(s, t); + } else { + for (int i = 0; i < 4; i++) { + sum += s[i] * t[i]; + } } d = my_isnan(sum) ? fabsf(sum) : sum; ApplyPrefixD(&d, V_Single); @@ -1437,9 +1441,13 @@ namespace MIPSInt u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + if (USE_VFPU_DOT) { + d = vfpu_dot(s, t); + } else { + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; + } } ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); @@ -1471,9 +1479,13 @@ namespace MIPSInt tprefixAdd = 0; ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d = 0.0f; - for (int i = 0; i < 4; i++) { - d += s[i] * t[i]; + if (USE_VFPU_DOT) { + d = vfpu_dot(s, t); + } else { + d = 0.0f; + for (int i = 0; i < 4; i++) { + d += s[i] * t[i]; + } } ApplyPrefixD(&d, V_Single); WriteVector(&d, V_Single, vd); From 08a894abdea0ac5e7ea3de69df2b6edd80cc11ee Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 12 Jun 2019 22:17:25 -0700 Subject: [PATCH 03/14] interp: Handle NaN better in vfpu_dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 1ad629d7c2..8de54a7834 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -635,6 +635,7 @@ float vfpu_dot(float a[4], float b[4]) { uint32_t exps[4]; int32_t mants[4]; uint32_t max_exp = 0; + uint32_t last_inf = 0; for (int i = 0; i < 4; i++) { exps[i] = get_uexp(intermed[i].i); @@ -643,6 +644,15 @@ float vfpu_dot(float a[4], float b[4]) { if (exps[i] > max_exp) { max_exp = exps[i]; } + if (exps[i] == 255) { + bool diff_sign = last_inf && get_sign(last_inf) != get_sign(intermed[i].i); + bool mant_nan = mants[i] != (0x00800000 << EXTRA_BITS); + if (diff_sign || mant_nan) { + intermed[0].i = 0x7F800001; + return intermed[0].f; + } + last_inf = intermed[i].i; + } } int32_t mant_sum = 0; From 2ba35c639170a8bd04af3a33a2a66c9566452fed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 14 Jun 2019 22:19:30 -0700 Subject: [PATCH 04/14] interp: Use an integer multiply for dots. --- Core/MIPS/MIPSVFPUUtils.cpp | 84 +++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 8de54a7834..186d458db6 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -15,9 +15,11 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include #include +#include "Common/BitScan.h" #include "Common/CommonFuncs.h" #include "Core/Reporting.h" #include "Core/MIPS/MIPS.h" @@ -626,32 +628,52 @@ float vfpu_dot(float a[4], float b[4]) { union float2int { uint32_t i; float f; - } intermed[4]; + }; + float2int result; + float2int src[2]; - for (int i = 0; i < 4; i++) { - intermed[i].f = a[i] * b[i]; - } - - uint32_t exps[4]; + int32_t exps[4]; int32_t mants[4]; - uint32_t max_exp = 0; - uint32_t last_inf = 0; + int32_t signs[4]; + int32_t max_exp = 0; + int32_t last_inf = -1; for (int i = 0; i < 4; i++) { - exps[i] = get_uexp(intermed[i].i); - // Preserve extra bits of precision in the mantissa during the add. - mants[i] = get_mant(intermed[i].i) << EXTRA_BITS; + src[0].f = a[i]; + src[1].f = b[i]; + + int32_t aexp = get_uexp(src[0].i); + int32_t bexp = get_uexp(src[1].i); + int32_t amant = get_mant(src[0].i) << EXTRA_BITS; + int32_t bmant = get_mant(src[1].i) << EXTRA_BITS; + + bool anan = aexp == 255 && (src[0].i & 0x007FFFFF) != 0; + bool bnan = bexp == 255 && (src[1].i & 0x007FFFFF) != 0; + if (anan || bnan) { + result.i = 0x7F800001; + return result.f; + } + + exps[i] = aexp + bexp - 127; + if (exps[i] >= 255) { + mants[i] = get_mant(0) << EXTRA_BITS; + } else { + // TODO: Adjust precision? + uint64_t adjust = (uint64_t)amant * (uint64_t)bmant; + mants[i] = (adjust >> (23 + EXTRA_BITS)) & 0x7FFFFFFF; + } + signs[i] = get_sign(src[0].i) ^ get_sign(src[1].i); + if (exps[i] > max_exp) { max_exp = exps[i]; } - if (exps[i] == 255) { - bool diff_sign = last_inf && get_sign(last_inf) != get_sign(intermed[i].i); - bool mant_nan = mants[i] != (0x00800000 << EXTRA_BITS); - if (diff_sign || mant_nan) { - intermed[0].i = 0x7F800001; - return intermed[0].f; + if (exps[i] >= 255) { + // Infinity minus infinity is not a real number. + if (last_inf != -1 && signs[i] != last_inf) { + result.i = 0x7F800001; + return result.f; } - last_inf = intermed[i].i; + last_inf = signs[i]; } } @@ -663,7 +685,7 @@ float vfpu_dot(float a[4], float b[4]) { } else { mants[i] >>= max_exp - exps[i]; } - if (get_sign(intermed[i].i)) { + if (signs[i]) { mants[i] = -mants[i]; } mant_sum += mants[i]; @@ -674,20 +696,20 @@ float vfpu_dot(float a[4], float b[4]) { sign_sum = 0x80000000; mant_sum = -mant_sum; } - // Chop off the extra bits. - mant_sum >>= EXTRA_BITS; + // Account for the mantissa actually having more precision. + max_exp -= EXTRA_BITS; - if (mant_sum == 0 || max_exp == 0) { + if (mant_sum == 0 || max_exp <= 0) { return 0.0f; } - while (mant_sum < 0x00800000) { - mant_sum <<= 1; - max_exp -= 1; - } - while (mant_sum >= 0x01000000) { - mant_sum >>= 1; - max_exp += 1; + int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; + if (shift < 0) { + mant_sum >>= -shift; + max_exp += -shift; + } else { + mant_sum <<= shift; + max_exp -= shift; } _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); @@ -698,6 +720,6 @@ float vfpu_dot(float a[4], float b[4]) { return 0.0f; } - intermed[0].i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); - return intermed[0].f; + result.i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); + return result.f; } From d5e572b82e3268e295582e6febdc9017c0264990 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Jun 2019 18:27:52 -0700 Subject: [PATCH 05/14] interp: Correct INF * 0 during dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 186d458db6..6215501cd2 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -647,15 +647,19 @@ float vfpu_dot(float a[4], float b[4]) { int32_t amant = get_mant(src[0].i) << EXTRA_BITS; int32_t bmant = get_mant(src[1].i) << EXTRA_BITS; - bool anan = aexp == 255 && (src[0].i & 0x007FFFFF) != 0; - bool bnan = bexp == 255 && (src[1].i & 0x007FFFFF) != 0; - if (anan || bnan) { - result.i = 0x7F800001; - return result.f; - } - exps[i] = aexp + bexp - 127; - if (exps[i] >= 255) { + if (aexp == 255) { + // INF * 0 = NAN + if ((src[0].i & 0x007FFFFF) != 0 || bexp == 0) { + result.i = 0x7F800001; + return result.f; + } + mants[i] = get_mant(0) << EXTRA_BITS; + } else if (bexp == 255) { + if ((src[1].i & 0x007FFFFF) != 0 || aexp == 0) { + result.i = 0x7F800001; + return result.f; + } mants[i] = get_mant(0) << EXTRA_BITS; } else { // TODO: Adjust precision? From c61a24cef01a4eb161ba019129e73c034e181ad5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 26 Jun 2019 21:32:32 -0700 Subject: [PATCH 06/14] interp: Handle rounding in software dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 6215501cd2..edd497d795 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -687,7 +687,7 @@ float vfpu_dot(float a[4], float b[4]) { if (exp >= 32) { mants[i] = 0; } else { - mants[i] >>= max_exp - exps[i]; + mants[i] >>= exp; } if (signs[i]) { mants[i] = -mants[i]; @@ -700,8 +700,9 @@ float vfpu_dot(float a[4], float b[4]) { sign_sum = 0x80000000; mant_sum = -mant_sum; } - // Account for the mantissa actually having more precision. - max_exp -= EXTRA_BITS; + + // Truncate off the extra bits now. We want to zero them for rounding purposes. + mant_sum >>= EXTRA_BITS; if (mant_sum == 0 || max_exp <= 0) { return 0.0f; @@ -709,13 +710,19 @@ float vfpu_dot(float a[4], float b[4]) { int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; if (shift < 0) { + // Round to even if we'd shift away a 0.5. + uint32_t round_bit = 1 << (-shift - 1); + if ((mant_sum & round_bit) && (mant_sum & (round_bit << 1))) { + mant_sum += round_bit; + shift = (int8_t)clz32_nonzero(mant_sum) - 8; + } mant_sum >>= -shift; max_exp += -shift; } else { mant_sum <<= shift; max_exp -= shift; } - _dbg_assert_(JIT, (mant_sum & 0x00800000) != 0); + _dbg_assert_msg_(JIT, (mant_sum & 0x00800000) != 0, "Mantissa wrong: %08x", mant_sum); if (max_exp >= 255) { max_exp = 255; From 7b84be1479450a555c17a0a5b0cd500eb2e82297 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 20:48:20 -0700 Subject: [PATCH 07/14] interp: Fix adding infinity in software dot. --- Core/MIPS/MIPSVFPUUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index edd497d795..7019429b53 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -655,12 +655,14 @@ float vfpu_dot(float a[4], float b[4]) { return result.f; } mants[i] = get_mant(0) << EXTRA_BITS; + exps[i] = 255; } else if (bexp == 255) { if ((src[1].i & 0x007FFFFF) != 0 || aexp == 0) { result.i = 0x7F800001; return result.f; } mants[i] = get_mant(0) << EXTRA_BITS; + exps[i] = 255; } else { // TODO: Adjust precision? uint64_t adjust = (uint64_t)amant * (uint64_t)bmant; From d2f7594dc5192d9ef4bf54fc634b5f0e8519cc10 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 21:31:36 -0700 Subject: [PATCH 08/14] interp: Use software dot in vcrsp/vqmul. Disabled by default using a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 48 +++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index e22e67fc5f..441b419340 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2129,27 +2129,61 @@ namespace MIPSInt switch (sz) { case V_Triple: // vcrsp.t - d[0] = s[1]*t[2] - s[2]*t[1]; - d[1] = s[2]*t[0] - s[0]*t[2]; + { + if (USE_VFPU_DOT) { + float t0[4] = { 0.0f, t[2], -t[1], 0.0f }; + float t1[4] = { -t[2], 0.0f, t[0], 0.0f }; + d[0] = vfpu_dot(s, t0); + d[1] = vfpu_dot(s, t1); + } else { + d[0] = s[1] * t[2] - s[2] * t[1]; + d[1] = s[2] * t[0] - s[0] * t[2]; + } // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, V_Quad); - d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) { + // TODO: But flush any infs to 0? This seems sketchy. + for (int i = 0; i < 4; ++i) { + if (my_isinf(s[i])) + s[i] = 0.0f; + if (my_isinf(t[i])) + t[i] = 0.0f; + } + d[2] = vfpu_dot(s, t); + } else { + d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + } break; + } case V_Quad: // vqmul.q - d[0] = s[0]*t[3] + s[1]*t[2] - s[2]*t[1] + s[3]*t[0]; - d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1]; - d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2]; + { + if (USE_VFPU_DOT) { + float t0[4] = { t[3], t[2], -t[1], t[0] }; + float t1[4] = { -t[2], t[3], t[0], t[1] }; + float t2[4] = { t[1], -t[0], t[3], t[2] }; + d[0] = vfpu_dot(s, t0); + d[1] = vfpu_dot(s, t1); + d[2] = vfpu_dot(s, t2); + } else { + d[0] = s[0] * t[3] + s[1] * t[2] - s[2] * t[1] + s[3] * t[0]; + d[1] = -s[0] * t[2] + s[1] * t[3] + s[2] * t[0] + s[3] * t[1]; + d[2] = s[0] * t[1] - s[1] * t[0] + s[2] * t[3] + s[3] * t[2]; + } // T prefix forces swizzle and negate, can be used to have weird constants. tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); ApplySwizzleS(s, sz); - d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) + d[3] = vfpu_dot(s, t); + else + d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3]; break; + } case V_Pair: // t swizzles invalid so the multiply is always zero. From ed4f0896db3561a608f91bd93fb7cb9cc0843dd3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:35:15 -0700 Subject: [PATCH 09/14] interp: Use vfpu_dot for vdet as well. Improves test accuracy here too. Also keep neg sign on NAN for vdiv. Disabled by default via compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 441b419340..8ecb5fac72 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -1419,8 +1419,14 @@ namespace MIPSInt u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 0, 0); ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad); - d[0] = s[0] * t[0] - s[1] * t[1]; - d[0] += s[2] * t[2] + s[3] * t[3]; + if (USE_VFPU_DOT) { + s[1] = -s[1]; + d[0] = vfpu_dot(s, t); + } else { + d[0] = s[0] * t[0] - s[1] * t[1]; + d[0] += s[2] * t[2] + s[3] * t[3]; + } + ApplyPrefixD(d, sz); WriteVector(d, V_Single, vd); PC += 4; @@ -2092,7 +2098,7 @@ namespace MIPSInt if (USE_VFPU_DOT) { if (my_isnan(d.f[i])) { - d.u[i] = 0x7f800001; + d.u[i] = (d.u[i] & 0xff800001) | 1; } else if ((d.u[i] & 0x7F800000) == 0) { d.u[i] &= 0xFF800000; } From 13278dc1f44788571f90e376f2af6ddc37cdbb75 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:35:59 -0700 Subject: [PATCH 10/14] interp: Implement software sqrt to match PSP. This matches the PSP's square root better. Disabled by default using a compile-time flag. --- Core/MIPS/MIPSIntVFPU.cpp | 5 ++-- Core/MIPS/MIPSVFPUUtils.cpp | 54 +++++++++++++++++++++++++++++++++++++ Core/MIPS/MIPSVFPUUtils.h | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 8ecb5fac72..6c1311483e 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -71,6 +71,7 @@ #endif static const bool USE_VFPU_DOT = false; +static const bool USE_VPFU_SQRT = false; union FloatBits { float f[4]; @@ -617,13 +618,13 @@ namespace MIPSInt case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 case 16: d[i] = 1.0f / s[i]; break; //vrcp - case 17: d[i] = 1.0f / sqrtf(s[i]); break; //vrsq + case 17: d[i] = USE_VPFU_SQRT ? 1.0f / vfpu_sqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos case 20: d[i] = powf(2.0f, s[i]); break; //vexp2 case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2 - case 22: d[i] = fabsf(sqrtf(s[i])); break; //vsqrt + case 22: d[i] = USE_VPFU_SQRT ? fabsf(vfpu_sqrt(s[i])) : fabsf(sqrtf(s[i])); break; //vsqrt case 23: d[i] = asinf(s[i]) / M_PI_2; break; //vasin case 24: d[i] = -1.0f / s[i]; break; // vnrcp case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 7019429b53..8d0d3e5e6f 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -614,6 +614,10 @@ static uint32_t get_uexp(uint32_t x) { return (x >> 23) & 0xFF; } +int32_t get_exp(uint32_t x) { + return get_uexp(x) - 127; +} + static int32_t get_mant(uint32_t x) { // Note: this returns the hidden 1. return (x & 0x007FFFFF) | 0x00800000; @@ -736,3 +740,53 @@ float vfpu_dot(float a[4], float b[4]) { result.i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF); return result.f; } + +// TODO: This is still not completely accurate compared to the PSP's vsqrt. +float vfpu_sqrt(float a) { + union float2int { + uint32_t u; + float f; + }; + float2int val; + val.f = a; + + if ((val.u & 0xff800000) == 0x7f800000) { + if ((val.u & 0x007fffff) != 0) { + val.u = 0x7f800001; + } + return val.f; + } + if ((val.u & 0x7fffffff) == 0) { + // Kill any sign. + val.u = 0; + return val.f; + } + if (val.u & 0x80000000) { + val.u = 0x7f800001; + return val.f; + } + + int k = get_exp(val.u); + int sp = get_mant(val.u); + int less_bits; + + if (k & 1) { + less_bits = 1; + k /= 2; + } else { + less_bits = 0; + k /= 2; + } + + int z = 0x00800000 >> less_bits; + int64_t halfsp = sp >> 1; + halfsp <<= 23 - less_bits; + for (int i = 0; i < 6; ++i) { + z = (z >> 1) + (int)(halfsp / z); + } + + val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); + // The lower two bits never end up set on the PSP, it seems like. + val.u &= 0xFFFFFFFC; + return val.f; +} diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 8e3d52bf70..53c729e3c0 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -98,6 +98,7 @@ inline float vfpu_clamp(float v, float min, float max) { } float vfpu_dot(float a[4], float b[4]); +float vfpu_sqrt(float a); #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 From 6028b798958f994a6fe780e180eb7910251fc289 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 8 Jul 2019 22:58:59 -0700 Subject: [PATCH 11/14] interp: Oops, fix vdot bug with subnormals. --- Core/MIPS/MIPSVFPUUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 8d0d3e5e6f..67f0bb5798 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -733,7 +733,7 @@ float vfpu_dot(float a[4], float b[4]) { if (max_exp >= 255) { max_exp = 255; mant_sum = 0; - } else if (max_exp == 0) { + } else if (max_exp <= 0) { return 0.0f; } From c1c869df2774422fe1c808d428c90b247c2768c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Jul 2019 14:09:20 -0700 Subject: [PATCH 12/14] interp: Implement software inverse square root. --- Core/MIPS/MIPSIntVFPU.cpp | 4 +- Core/MIPS/MIPSVFPUUtils.cpp | 88 +++++++++++++++++++++++++++++++------ Core/MIPS/MIPSVFPUUtils.h | 1 + 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 6c1311483e..cf5edba112 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -618,13 +618,13 @@ namespace MIPSInt case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 case 16: d[i] = 1.0f / s[i]; break; //vrcp - case 17: d[i] = USE_VPFU_SQRT ? 1.0f / vfpu_sqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq + case 17: d[i] = USE_VPFU_SQRT ? vfpu_rsqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos case 20: d[i] = powf(2.0f, s[i]); break; //vexp2 case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2 - case 22: d[i] = USE_VPFU_SQRT ? fabsf(vfpu_sqrt(s[i])) : fabsf(sqrtf(s[i])); break; //vsqrt + case 22: d[i] = USE_VPFU_SQRT ? vfpu_sqrt(s[i]) : fabsf(sqrtf(s[i])); break; //vsqrt case 23: d[i] = asinf(s[i]) / M_PI_2; break; //vasin case 24: d[i] = -1.0f / s[i]; break; // vnrcp case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 67f0bb5798..7e46209c76 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -717,10 +717,13 @@ float vfpu_dot(float a[4], float b[4]) { int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8; if (shift < 0) { // Round to even if we'd shift away a 0.5. - uint32_t round_bit = 1 << (-shift - 1); + const uint32_t round_bit = 1 << (-shift - 1); if ((mant_sum & round_bit) && (mant_sum & (round_bit << 1))) { mant_sum += round_bit; shift = (int8_t)clz32_nonzero(mant_sum) - 8; + } else if ((mant_sum & round_bit) && (mant_sum & (round_bit - 1))) { + mant_sum += round_bit; + shift = (int8_t)clz32_nonzero(mant_sum) - 8; } mant_sum >>= -shift; max_exp += -shift; @@ -756,7 +759,7 @@ float vfpu_sqrt(float a) { } return val.f; } - if ((val.u & 0x7fffffff) == 0) { + if ((val.u & 0x7f800000) == 0) { // Kill any sign. val.u = 0; return val.f; @@ -767,26 +770,83 @@ float vfpu_sqrt(float a) { } int k = get_exp(val.u); - int sp = get_mant(val.u); - int less_bits; + uint32_t sp = get_mant(val.u); + int less_bits = k & 1; + k >>= 1; - if (k & 1) { - less_bits = 1; - k /= 2; - } else { - less_bits = 0; - k /= 2; - } - - int z = 0x00800000 >> less_bits; + uint32_t z = 0x00C00000 >> less_bits; int64_t halfsp = sp >> 1; halfsp <<= 23 - less_bits; for (int i = 0; i < 6; ++i) { - z = (z >> 1) + (int)(halfsp / z); + z = (z >> 1) + (uint32_t)(halfsp / z); } val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF); // The lower two bits never end up set on the PSP, it seems like. val.u &= 0xFFFFFFFC; + + return val.f; +} + +static inline uint32_t mant_mul(uint32_t a, uint32_t b) { + uint64_t m = (uint64_t)a * (uint64_t)b; + if (m & 0x007FFFFF) { + m += 0x01437000; + } + return m >> 23; +} + +float vfpu_rsqrt(float a) { + union float2int { + uint32_t u; + float f; + }; + float2int val; + val.f = a; + + if (val.u == 0x7f800000) { + return 0.0f; + } + if ((val.u & 0x7fffffff) > 0x7f800000) { + val.u = (val.u & 0x80000000) | 0x7f800001; + return val.f; + } + if ((val.u & 0x7f800000) == 0) { + val.u = (val.u & 0x80000000) | 0x7f800000; + return val.f; + } + if (val.u & 0x80000000) { + val.u = 0xff800001; + return val.f; + } + + int k = get_exp(val.u); + uint32_t sp = get_mant(val.u); + int less_bits = k & 1; + k = -(k >> 1); + + uint32_t z = 0x00800000 >> less_bits; + uint32_t halfsp = sp >> (1 + less_bits); + for (int i = 0; i < 6; ++i) { + uint32_t oldz = z; + uint32_t zsq = mant_mul(z, z); + uint32_t correction = 0x00C00000 - mant_mul(halfsp, zsq); + z = mant_mul(z, correction); + } + + int8_t shift = (int8_t)clz32_nonzero(z) - 8 + less_bits; + if (shift < 1) { + z >>= -shift; + k += -shift; + } else if (shift > 0) { + z <<= shift; + k -= shift; + } + + z >>= less_bits; + + val.u = ((k + 127) << 23) | (z & 0x007FFFFF); + val.u &= 0xFFFFFFFC; + return val.f; } diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 53c729e3c0..cf7c1e7426 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -99,6 +99,7 @@ inline float vfpu_clamp(float v, float min, float max) { float vfpu_dot(float a[4], float b[4]); float vfpu_sqrt(float a); +float vfpu_rsqrt(float a); #define VFPU_FLOAT16_EXP_MAX 0x1f #define VFPU_SH_FLOAT16_SIGN 15 From f65a71d6d81e1aa4c820abb5976aabf2987ddc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 4 Aug 2019 21:37:33 -0700 Subject: [PATCH 13/14] Compat: Option to use accurate dotprod for VMMUL. Eliminates Tekken 6 leg shaking. --- Common/BitScan.h | 1 + Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + Core/MIPS/ARM/ArmCompVFPU.cpp | 14 ++++++++++---- Core/MIPS/ARM64/Arm64CompVFPU.cpp | 13 ++++++++++--- Core/MIPS/IR/IRCompVFPU.cpp | 8 ++++++++ Core/MIPS/MIPSIntVFPU.cpp | 8 ++++++-- Core/MIPS/x86/CompVFPU.cpp | 15 +++++++++++---- assets/compat.ini | 6 ++++++ 9 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Common/BitScan.h b/Common/BitScan.h index 729c320759..4a8a8c28ac 100644 --- a/Common/BitScan.h +++ b/Common/BitScan.h @@ -1,6 +1,7 @@ #pragma once #include "ppsspp_config.h" +#include #if PPSSPP_PLATFORM(WINDOWS) #include "Common/CommonWindows.h" diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 71f67ebe4a..4763cf4c25 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -65,6 +65,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS); CheckSetting(iniFile, gameID, "JitInvalidationHack", &flags_.JitInvalidationHack); CheckSetting(iniFile, gameID, "HideISOFiles", &flags_.HideISOFiles); + CheckSetting(iniFile, gameID, "MoreAccurateVMMUL", &flags_.MoreAccurateVMMUL); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index c85267cf46..8632813e26 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -65,6 +65,7 @@ struct CompatFlags { bool ForceMax60FPS; bool JitInvalidationHack; bool HideISOFiles; + bool MoreAccurateVMMUL; }; class IniFile; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 4893820081..94cd0d811c 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -21,14 +21,16 @@ #include #include "math/math_util.h" +#include "Core/Compatibility.h" +#include "Core/Config.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Common/CPUDetect.h" -#include "Core/Config.h" -#include "Core/Reporting.h" #include "Core/MIPS/ARM/ArmJit.h" #include "Core/MIPS/ARM/ArmRegCache.h" @@ -1468,12 +1470,16 @@ namespace MIPSComp void ArmJit::Comp_Vmmul(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX_VMMUL); - if (js.HasUnknownPrefix()) { + if (!js.HasNoPrefix()) { DISABLE; } NEON_IF_AVAILABLE(CompNEON_Vmmul); - // TODO: This probably ignores prefixes? + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } MatrixSize sz = GetMtxSize(op); int n = GetMatrixSide(sz); diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 243890f1ea..69d6a302c6 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -21,15 +21,16 @@ #include #include "math/math_util.h" +#include "Core/Compatibility.h" +#include "Core/Config.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Common/CPUDetect.h" -#include "Core/Config.h" -#include "Core/Reporting.h" - #include "Common/Arm64Emitter.h" #include "Core/MIPS/ARM64/Arm64Jit.h" #include "Core/MIPS/ARM64/Arm64RegCache.h" @@ -1219,6 +1220,12 @@ namespace MIPSComp { DISABLE; } + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } + MatrixSize sz = GetMtxSize(op); int n = GetMatrixSide(sz); diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index fe69d4f7e1..13fa39b2c2 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -20,6 +20,7 @@ #include "math/math_util.h" #include "Common/CPUDetect.h" +#include "Core/Compatibility.h" #include "Core/Config.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" @@ -29,6 +30,7 @@ #include "Core/MIPS/IR/IRFrontend.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/Reporting.h" +#include "Core/System.h" // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. @@ -1242,6 +1244,12 @@ namespace MIPSComp { DISABLE; } + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } + // Matrix multiply (weird prefixes) // D[0 .. N, 0 .. M] = S[0 .. N, 0 .. M]' * T[0 .. N, 0 .. M] // Note: Behaves as if it's implemented through a series of vdots. diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index cf5edba112..8e3ad0ba01 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -23,9 +23,11 @@ #include "math/math_util.h" +#include "Core/Compatibility.h" #include "Core/Core.h" -#include "Core/Reporting.h" #include "Core/MemMap.h" +#include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSInt.h" @@ -467,6 +469,8 @@ namespace MIPSInt ReadMatrix(s, sz, vs); ReadMatrix(t, sz, vt); + // TODO: Always use the more accurate path in interpreter? + bool useAccurateDot = USE_VFPU_DOT || PSP_CoreParameter().compat.flags().MoreAccurateVMMUL; for (int a = 0; a < n; a++) { for (int b = 0; b < n; b++) { union { float f; uint32_t u; } sum = { 0.0f }; @@ -476,7 +480,7 @@ namespace MIPSInt ApplySwizzleT(&t[a * 4], V_Quad); } - if (USE_VFPU_DOT) { + if (useAccurateDot) { sum.f = vfpu_dot(&s[b * 4], &t[a * 4]); if (my_isnan(sum.f)) { sum.u = 0x7f800001; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index 0b87ef5ea0..ff7bd79f2b 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -29,9 +29,11 @@ #include "math/math_util.h" #include "Common/CPUDetect.h" -#include "Core/MemMap.h" +#include "Core/Compatibility.h" #include "Core/Config.h" +#include "Core/MemMap.h" #include "Core/Reporting.h" +#include "Core/System.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSVFPUUtils.h" @@ -2803,10 +2805,15 @@ void Jit::Comp_VScl(MIPSOpcode op) { void Jit::Comp_Vmmul(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_MTX_VMMUL); - - // TODO: This probably ignores prefixes? - if (js.HasUnknownPrefix()) + if (!js.HasNoPrefix()) { DISABLE; + } + + if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) { + // Fall back to interpreter, which has the accurate implementation. + // Later we might do something more optimized here. + DISABLE; + } MatrixSize sz = GetMtxSize(op); VectorSize vsz = GetVectorSize(sz); diff --git a/assets/compat.ini b/assets/compat.ini index 15aee5a550..74cd2406d5 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -620,3 +620,9 @@ NPJH50471 = true ULJM06033 = true NPJH50559 = true NPEH00030 = true + +[MoreAccurateVMMUL] +# Fixes leg shaking in Tekken 6. The potential for slowdown in other games is large enough +# that we will not generally apply this accurate mode where not needed. +ULES01376 = true +ULUS10466 = true From 85b9ad8fe3565a225dbbd94636a6bb5b9feaaab9 Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Sun, 4 Aug 2019 21:41:25 -0700 Subject: [PATCH 14/14] Compat: Add more Tekken game IDs to leg shake fix. --- assets/compat.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/compat.ini b/assets/compat.ini index 74cd2406d5..f1917c5882 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -624,5 +624,10 @@ NPEH00030 = true [MoreAccurateVMMUL] # Fixes leg shaking in Tekken 6. The potential for slowdown in other games is large enough # that we will not generally apply this accurate mode where not needed. -ULES01376 = true ULUS10466 = true +ULES01376 = true +ULJS00224 = true +NPUH10047 = true +ULAS42214 = true +ULJS19054 = true +NPJH50184 = true