Merge pull request #12217 from unknownbrackets/vfpu-dot

Merge vfpu-dot changes and add compat flag for Tekken
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2019-08-05 23:49:38 +02:00
commit 2ba998d007
11 files changed
+479 -64

No files matched your search

+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "ppsspp_config.h"
#include <cstdint>
#if PPSSPP_PLATFORM(WINDOWS)
#include "Common/CommonWindows.h"
+1
View File
@@ -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) {
+1
View File
@@ -65,6 +65,7 @@ struct CompatFlags {
bool ForceMax60FPS;
bool JitInvalidationHack;
bool HideISOFiles;
bool MoreAccurateVMMUL;
};
class IniFile;
+10 -4
View File
@@ -21,14 +21,16 @@
#include <cmath>
#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);
+10 -3
View File
@@ -21,15 +21,16 @@
#include <cmath>
#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);
+8
View File
@@ -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.
+179 -53
View File
@@ -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"
@@ -70,6 +72,9 @@
#define M_SQRT1_2 0.707106781186547524401f
#endif
static const bool USE_VFPU_DOT = false;
static const bool USE_VPFU_SQRT = false;
union FloatBits {
float f[4];
u32 u[4];
@@ -464,22 +469,37 @@ 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++) {
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 (useAccurateDot) {
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;
}
}
@@ -602,13 +622,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 ? 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] = 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
@@ -1123,7 +1143,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 +1152,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();
}
@@ -1173,8 +1204,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);
@@ -1389,8 +1424,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;
@@ -1411,9 +1452,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);
@@ -1445,9 +1490,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);
@@ -1565,7 +1614,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 +1629,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 +1683,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 +2050,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 +2095,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] = (d.u[i] & 0xff800001) | 1;
} else if ((d.u[i] & 0x7F800000) == 0) {
d.u[i] &= 0xFF800000;
}
}
}
@@ -2023,12 +2115,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();
}
@@ -2048,27 +2140,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.
+243
View File
@@ -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 <cstdint>
#include <limits>
#include <stdio.h>
#include "Common/BitScan.h"
#include "Common/CommonFuncs.h"
#include "Core/Reporting.h"
#include "Core/MIPS/MIPS.h"
@@ -607,3 +609,244 @@ float Float16ToFloat32(unsigned short l)
}
return f;
}
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;
}
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;
};
float2int result;
float2int src[2];
int32_t exps[4];
int32_t mants[4];
int32_t signs[4];
int32_t max_exp = 0;
int32_t last_inf = -1;
for (int i = 0; i < 4; i++) {
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;
exps[i] = aexp + bexp - 127;
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;
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;
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) {
// Infinity minus infinity is not a real number.
if (last_inf != -1 && signs[i] != last_inf) {
result.i = 0x7F800001;
return result.f;
}
last_inf = signs[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] >>= exp;
}
if (signs[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;
}
// 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;
}
int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8;
if (shift < 0) {
// Round to even if we'd shift away a 0.5.
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;
} else {
mant_sum <<= shift;
max_exp -= shift;
}
_dbg_assert_msg_(JIT, (mant_sum & 0x00800000) != 0, "Mantissa wrong: %08x", mant_sum);
if (max_exp >= 255) {
max_exp = 255;
mant_sum = 0;
} else if (max_exp <= 0) {
return 0.0f;
}
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 & 0x7f800000) == 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);
uint32_t sp = get_mant(val.u);
int less_bits = k & 1;
k >>= 1;
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) + (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;
}
+4
View File
@@ -97,6 +97,10 @@ 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]);
float vfpu_sqrt(float a);
float vfpu_rsqrt(float a);
#define VFPU_FLOAT16_EXP_MAX 0x1f
#define VFPU_SH_FLOAT16_SIGN 15
#define VFPU_MASK_FLOAT16_SIGN 0x1
+11 -4
View File
@@ -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);
+11
View File
@@ -620,3 +620,14 @@ 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.
ULUS10466 = true
ULES01376 = true
ULJS00224 = true
NPUH10047 = true
ULAS42214 = true
ULJS19054 = true
NPJH50184 = true