diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 10b2c42896cc..fafdd6878b7e 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -75,6 +75,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "MemstickFixedFree", &flags_.MemstickFixedFree); CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited); CheckSetting(iniFile, gameID, "ReinterpretFramebuffers", &flags_.ReinterpretFramebuffers); + CheckSetting(iniFile, gameID, "DoublePrecisionSinCos", &flags_.DoublePrecisionSinCos); } 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 e390e28f424f..415e589ad948 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -73,6 +73,7 @@ struct CompatFlags { bool MemstickFixedFree; bool DateLimited; bool ReinterpretFramebuffers; + bool DoublePrecisionSinCos; }; class IniFile; diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 923faf804dc9..917fa16a042f 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -895,3 +895,86 @@ float vfpu_rsqrt(float a) { return val.f; } + +float vfpu_sin_single(float angle) { + angle -= floorf(angle * 0.25f) * 4.f; + if (angle == 0.0f || angle == 2.0f) { + return 0.0f; + } else if (angle == 1.0f) { + return 1.0f; + } else if (angle == 3.0f) { + return -1.0f; + } + angle *= (float)M_PI_2; + return sinf(angle); +} + +float vfpu_cos_single(float angle) { + angle -= floorf(angle * 0.25f) * 4.f; + if (angle == 1.0f || angle == 3.0f) { + return 0.0f; + } else if (angle == 0.0f) { + return 1.0f; + } else if (angle == 2.0f) { + return -1.0f; + } + angle *= (float)M_PI_2; + return cosf(angle); +} + +void vfpu_sincos_single(float angle, float &sine, float &cosine) { + angle -= floorf(angle * 0.25f) * 4.f; + if (angle == 0.0f) { + sine = 0.0f; + cosine = 1.0f; + } else if (angle == 1.0f) { + sine = 1.0f; + cosine = 0.0f; + } else if (angle == 2.0f) { + sine = 0.0f; + cosine = -1.0f; + } else if (angle == 3.0f) { + sine = -1.0f; + cosine = 0.0f; + } else { + angle *= (float)M_PI_2; +#if defined(__linux__) + sincosf(angle, &sine, &cosine); +#else + sine = sinf(angle); + cosine = cosf(angle); +#endif + } +} + +float vfpu_sin_double(float angle) { + return (float)sin((double)angle * M_PI_2); +} + +float vfpu_cos_double(float angle) { + return (float)cos((double)angle * M_PI_2); +} + +void vfpu_sincos_double(float angle_f, float &sine, float &cosine) { + double angle = (double)angle_f * M_PI_2; +#if defined(__linux__) + double d_sine; + double d_cosine; + sincos(angle, &d_sine, &d_cosine); + sine = (float)d_sine; + cosine = (float)d_cosine; +#else + sine = (float)sin(angle); + cosine = (float)cos(angle); +#endif +} + +float (*vfpu_sin)(float); +float (*vfpu_cos)(float); +void (*vfpu_sincos)(float, float&, float&); + +void InitVFPUSinCos(bool useDoublePrecision) { + vfpu_sin = useDoublePrecision ? vfpu_sin_double : vfpu_sin_single; + vfpu_cos = useDoublePrecision ? vfpu_cos_double : vfpu_cos_single; + vfpu_sincos = useDoublePrecision ? vfpu_sincos_double : vfpu_sincos_single; +} diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index e43c588a4178..b89335a25717 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -35,44 +35,29 @@ inline int Xpose(int v) { #endif // The VFPU uses weird angles where 4.0 represents a full circle. This makes it possible to return -// exact 1.0/-1.0 values at certain angles. We get close enough for #2921 and #12900 by computing -// things in double precision, multiplying the input by pi/2. +// exact 1.0/-1.0 values at certain angles. We currently just scale, and special case the cardinal directions. +// +// Stepping down to [0, 2pi) helps, but we also check common exact-result values. +// TODO: cos(1) and sin(2) should be -0.0, but doing that gives wrong results (possibly from floorf.) +// +// We also try an alternative solution, computing things in double precision, multiplying the input by pi/2. +// This fixes #12900 (Hitman Reborn 2) but breaks #13705 (Cho Aniki Zero) and #13671 (Hajime no Ippo). +// #2921 is still fine. So the alt solution (vfpu_sin_double etc) are behind a compat flag. // // A better solution would be to tailor some sine approximation for the 0..90 degrees range, compute // modulo manually and mirror that around the circle. Also correctly special casing for inf/nan inputs // and just trying to match it as closely as possible to the real PSP. // -// Stepping down to [0, 2pi) helps, but we also check common exact-result values. -// TODO: cos(1) and sin(2) should be -0.0, but doing that gives wrong results (possibly from floorf.) - // Messing around with the modulo functions? try https://www.desmos.com/calculator. -inline float vfpu_sin(float angle) { - return (float)sin((double)angle * M_PI_2); -} - -inline float vfpu_cos(float angle) { - return (float)cos((double)angle * M_PI_2); -} +extern float (*vfpu_sin)(float); +extern float (*vfpu_cos)(float); +extern void (*vfpu_sincos)(float, float&, float&); inline float vfpu_asin(float angle) { return asinf(angle) / M_PI_2; } -inline void vfpu_sincos(float angle_f, float &sine, float &cosine) { - double angle = (double)angle_f * M_PI_2; -#if defined(__linux__) - double d_sine; - double d_cosine; - sincos(angle, &d_sine, &d_cosine); - sine = (float)d_sine; - cosine = (float)d_cosine; -#else - sine = (float)sin(angle); - cosine = (float)cos(angle); -#endif -} - inline float vfpu_clamp(float v, float min, float max) { // Note: NAN is preserved, and -0.0 becomes +0.0 if min=+0.0. return v >= max ? max : (v <= min ? min : v); @@ -230,3 +215,4 @@ int GetVectorOverlap(int reg1, VectorSize size1, int reg2, VectorSize size2); bool GetVFPUCtrlMask(int reg, u32 *mask); float Float16ToFloat32(unsigned short l); +void InitVFPUSinCos(bool useDoublePrecision); diff --git a/Core/System.cpp b/Core/System.cpp index 736499a7f4b4..3b3e070bca0c 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -44,6 +44,7 @@ #include "Core/HDRemaster.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSAnalyst.h" +#include "Core/MIPS/MIPSVFPUUtils.h" #include "Core/Debugger/SymbolMap.h" #include "Core/Host.h" #include "Core/System.h" @@ -66,7 +67,6 @@ #include "Common/LogManager.h" #include "Common/ExceptionHandlerSetup.h" #include "Core/HLE/sceAudiocodec.h" - #include "GPU/GPUState.h" #include "GPU/GPUInterface.h" #include "GPU/Debugger/RecordFormat.h" @@ -287,6 +287,8 @@ bool CPU_Init() { // likely to collide with any commercial ones. coreParameter.compat.Load(discID); + InitVFPUSinCos(coreParameter.compat.flags().DoublePrecisionSinCos); + HLEPlugins::Init(); if (!Memory::Init()) { // We're screwed. diff --git a/assets/compat.ini b/assets/compat.ini index 2273beb0f376..1aac5a6dd941 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -808,3 +808,6 @@ ULKS46087 = true # ULJM05533 = true # NPJH50006 = true +[DoublePrecisionSinCos] +# Hitman Reborn Battle Arena 2 (#12900) +ULJS00218 = true