PowerPC: Set host CPU rounding mode on init and savestate

Not doing this can cause desyncs when TASing. (I don't know
how common such desyncs would be, though. For games that
don't change rounding modes, they shouldn't be a problem.)
This commit is contained in:
JosJuice 2021-06-10 19:54:07 +02:00
parent 8f9bb5612a
commit 9db0ebd4b6
9 changed files with 26 additions and 31 deletions

View File

@ -41,10 +41,6 @@ void SetRoundMode(int mode)
// We don't need to do anything here since SetSIMDMode is always called after calling this // We don't need to do anything here since SetSIMDMode is always called after calling this
} }
void SetPrecisionMode(PrecisionMode mode)
{
}
void SetSIMDMode(int rounding_mode, bool non_ieee_mode) void SetSIMDMode(int rounding_mode, bool non_ieee_mode)
{ {
// When AH is disabled, FZ controls flush-to-zero for both inputs and outputs. When AH is enabled, // When AH is disabled, FZ controls flush-to-zero for both inputs and outputs. When AH is enabled,

View File

@ -29,8 +29,6 @@ enum PrecisionMode
void SetRoundMode(int mode); void SetRoundMode(int mode);
void SetPrecisionMode(PrecisionMode mode);
void SetSIMDMode(int rounding_mode, bool non_ieee_mode); void SetSIMDMode(int rounding_mode, bool non_ieee_mode);
/* /*

View File

@ -11,9 +11,6 @@ namespace FPURoundMode
void SetRoundMode(int mode) void SetRoundMode(int mode)
{ {
} }
void SetPrecisionMode(PrecisionMode mode)
{
}
void SetSIMDMode(int rounding_mode, bool non_ieee_mode) void SetSIMDMode(int rounding_mode, bool non_ieee_mode)
{ {
} }

View File

@ -22,11 +22,6 @@ void SetRoundMode(int mode)
fesetround(rounding_mode_lut[mode]); fesetround(rounding_mode_lut[mode]);
} }
void SetPrecisionMode(PrecisionMode /* mode */)
{
// x64 doesn't need this - fpu is done with SSE
}
void SetSIMDMode(int rounding_mode, bool non_ieee_mode) void SetSIMDMode(int rounding_mode, bool non_ieee_mode)
{ {
// OR-mask for disabling FPU exceptions (bits 7-12 in the MXCSR register) // OR-mask for disabling FPU exceptions (bits 7-12 in the MXCSR register)

View File

@ -25,6 +25,7 @@
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/FPURoundMode.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
@ -625,6 +626,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
// thread, and then takes over and becomes the video thread // thread, and then takes over and becomes the video thread
Common::SetCurrentThreadName("Video thread"); Common::SetCurrentThreadName("Video thread");
UndeclareAsCPUThread(); UndeclareAsCPUThread();
FPURoundMode::LoadDefaultSIMDState();
// Spawn the CPU thread. The CPU thread will signal the event that boot is complete. // Spawn the CPU thread. The CPU thread will signal the event that boot is complete.
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path, delete_savestate); s_cpu_thread = std::thread(cpuThreadFunc, savestate_path, delete_savestate);

View File

@ -87,6 +87,10 @@ static void ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock)
void Run() void Run()
{ {
// Updating the host CPU's rounding mode must be done on the CPU thread.
// We can't rely on PowerPC::Init doing it, since it's called from EmuThread.
PowerPC::RoundingModeUpdated();
std::unique_lock state_lock(s_state_change_lock); std::unique_lock state_lock(s_state_change_lock);
while (s_state != State::PowerDown) while (s_state != State::PowerDown)
{ {

View File

@ -6,7 +6,6 @@
#include "Common/Assert.h" #include "Common/Assert.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FPURoundMode.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/HW/GPFifo.h" #include "Core/HW/GPFifo.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
@ -26,13 +25,10 @@ mffsx: 80036608
mffsx: 80036650 (huh?) mffsx: 80036650 (huh?)
*/ */
// TODO(ector): More proper handling of SSE state.
// That is, set rounding mode etc when entering jit code or the interpreter loop
// Restore rounding mode when calling anything external
static void FPSCRtoFPUSettings(UReg_FPSCR fp) static void FPSCRUpdated(UReg_FPSCR fp)
{ {
FPURoundMode::SetRoundMode(fp.RN); PowerPC::RoundingModeUpdated();
if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE) if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE)
{ {
@ -40,9 +36,6 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
// fp.VE, fp.OE, fp.UE, fp.ZE, fp.XE); // fp.VE, fp.OE, fp.UE, fp.ZE, fp.XE);
// Pokemon Colosseum does this. Gah. // Pokemon Colosseum does this. Gah.
} }
// Set SSE rounding mode and denormal handling
FPURoundMode::SetSIMDMode(fp.RN, fp.NI);
} }
static void UpdateFPSCR(UReg_FPSCR* fpscr) static void UpdateFPSCR(UReg_FPSCR* fpscr)
@ -57,7 +50,7 @@ void Interpreter::mtfsb0x(UGeckoInstruction inst)
u32 b = 0x80000000 >> inst.CRBD; u32 b = 0x80000000 >> inst.CRBD;
FPSCR.Hex &= ~b; FPSCR.Hex &= ~b;
FPSCRtoFPUSettings(FPSCR); FPSCRUpdated(FPSCR);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -74,7 +67,7 @@ void Interpreter::mtfsb1x(UGeckoInstruction inst)
else else
FPSCR |= b; FPSCR |= b;
FPSCRtoFPUSettings(FPSCR); FPSCRUpdated(FPSCR);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -89,7 +82,7 @@ void Interpreter::mtfsfix(UGeckoInstruction inst)
FPSCR = (FPSCR.Hex & ~mask) | (imm >> (4 * field)); FPSCR = (FPSCR.Hex & ~mask) | (imm >> (4 * field));
FPSCRtoFPUSettings(FPSCR); FPSCRUpdated(FPSCR);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();
@ -106,7 +99,7 @@ void Interpreter::mtfsfx(UGeckoInstruction inst)
} }
FPSCR = (FPSCR.Hex & ~m) | (static_cast<u32>(rPS(inst.FB).PS0AsU64()) & m); FPSCR = (FPSCR.Hex & ~m) | (static_cast<u32>(rPS(inst.FB).PS0AsU64()) & m);
FPSCRtoFPUSettings(FPSCR); FPSCRUpdated(FPSCR);
if (inst.Rc) if (inst.Rc)
PowerPC::ppcState.UpdateCR1(); PowerPC::ppcState.UpdateCR1();

View File

@ -20,6 +20,7 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h" #include "Core/CoreTiming.h"
#include "Core/HW/CPU.h" #include "Core/HW/CPU.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
@ -129,6 +130,7 @@ void DoState(PointerWrap& p)
if (p.GetMode() == PointerWrap::MODE_READ) if (p.GetMode() == PointerWrap::MODE_READ)
{ {
RoundingModeUpdated();
IBATUpdated(); IBATUpdated();
DBATUpdated(); DBATUpdated();
} }
@ -180,6 +182,7 @@ static void ResetRegisters()
} }
SetXER({}); SetXER({});
RoundingModeUpdated();
DBATUpdated(); DBATUpdated();
IBATUpdated(); IBATUpdated();
@ -246,10 +249,6 @@ CPUCore DefaultCPUCore()
void Init(CPUCore cpu_core) void Init(CPUCore cpu_core)
{ {
// NOTE: This function runs on EmuThread, not the CPU Thread.
// Changing the rounding mode has a limited effect.
FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53);
s_invalidate_cache_thread_safe = s_invalidate_cache_thread_safe =
CoreTiming::RegisterEvent("invalidateEmulatedCache", InvalidateCacheThreadSafe); CoreTiming::RegisterEvent("invalidateEmulatedCache", InvalidateCacheThreadSafe);
@ -632,4 +631,13 @@ void UpdateFPRF(double dvalue)
FPSCR.FPRF = Common::ClassifyDouble(dvalue); FPSCR.FPRF = Common::ClassifyDouble(dvalue);
} }
void RoundingModeUpdated()
{
// The rounding mode is separate for each thread, so this must run on the CPU thread
ASSERT(Core::IsCPUThread());
FPURoundMode::SetRoundMode(FPSCR.RN);
FPURoundMode::SetSIMDMode(FPSCR.RN, FPSCR.NI);
}
} // namespace PowerPC } // namespace PowerPC

View File

@ -306,4 +306,6 @@ inline void SetXER_OV(bool value)
void UpdateFPRF(double dvalue); void UpdateFPRF(double dvalue);
void RoundingModeUpdated();
} // namespace PowerPC } // namespace PowerPC