Skip to content

Commit

Permalink
Only use double precision sincos in Hitman Reborn Battle Arena 2. See #…
Browse files Browse the repository at this point in the history
  • Loading branch information
hrydgard committed Nov 23, 2020
1 parent 3db4c0b commit 9e4c7c8
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 56 deletions.
1 change: 1 addition & 0 deletions Core/Compatibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions Core/Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct CompatFlags {
bool MemstickFixedFree;
bool DateLimited;
bool ReinterpretFramebuffers;
bool DoublePrecisionSinCos;
};

class IniFile;
Expand Down
83 changes: 83 additions & 0 deletions Core/MIPS/MIPSVFPUUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
67 changes: 12 additions & 55 deletions Core/MIPS/MIPSVFPUUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,73 +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) {
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);
}

inline float vfpu_cos(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);
}
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, 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
}
}

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);
Expand Down Expand Up @@ -259,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);
4 changes: 3 additions & 1 deletion Core/System.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions assets/compat.ini
Original file line number Diff line number Diff line change
Expand Up @@ -808,3 +808,6 @@ ULKS46087 = true
# ULJM05533 = true
# NPJH50006 = true

[DoublePrecisionSinCos]
# Hitman Reborn Battle Arena 2 (#12900)
ULJS00218 = true

0 comments on commit 9e4c7c8

Please sign in to comment.