PPU: Add accurate vector estimates option

vrefp and vrsqrtefp are hardware exact only when enabled.
vexptefp and vlogefp are now exact by default.
This commit is contained in:
izcarti
2026-10-10 22:06:19 +03:00
committed by Elad
parent 63ad856afa
commit 31bd702e39
9 changed files with 60 additions and 12 deletions
+31 -10
View File
@@ -65,6 +65,7 @@ enum class ppu_exec_bit : u64
fix_nj,
set_vnan,
fix_vnan,
set_vest,
set_fpcc,
use_dfma,
set_cr_stats,
@@ -2177,17 +2178,26 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VREFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<>();
return ppu_exec_select<Flags...>::template select<set_vest, use_nj, fix_nj, set_vnan, fix_vnan>();
static const auto exec = [](auto&& d, auto&& b, auto&& nj)
static const auto exec = [](auto&& d, auto&& b_, auto&& jm_mask, auto&& nj)
{
for (u32 i = 0; i < 4; i++)
if constexpr (((Flags == set_vest) || ...))
{
d._u32[i] = ppu_vrefp(b._u32[i], nj);
for (u32 i = 0; i < 4; i++)
{
d._u32[i] = ppu_vrefp(b_._u32[i], nj);
}
}
else
{
auto m = gv_bcst32(jm_mask, &ppu_thread::jm_mask);
auto b = ppu_flush_denormal<false, Flags...>(m, std::move(b_));
d = ppu_flush_denormal<true, Flags...>(std::move(m), ppu_set_vnan<Flags...>(gv_divfs(gv_bcstfs(1.0f), b), b));
}
};
RETURN_(ppu.vr[op.vd], ppu.vr[op.vb], ppu.nj);
RETURN_(ppu.vr[op.vd], ppu.vr[op.vb], ppu.jm_mask, ppu.nj);
}
template <u32 Build, ppu_exec_bit... Flags>
@@ -2298,17 +2308,26 @@ template <u32 Build, ppu_exec_bit... Flags>
auto VRSQRTEFP()
{
if constexpr (Build == 0xf1a6)
return ppu_exec_select<Flags...>::template select<>();
return ppu_exec_select<Flags...>::template select<set_vest, use_nj, fix_nj, set_vnan, fix_vnan>();
static const auto exec = [](auto&& d, auto&& b, auto&& nj)
static const auto exec = [](auto&& d, auto&& b_, auto&& jm_mask, auto&& nj)
{
for (u32 i = 0; i < 4; i++)
if constexpr (((Flags == set_vest) || ...))
{
d._u32[i] = ppu_vrsqrtefp(b._u32[i], nj);
for (u32 i = 0; i < 4; i++)
{
d._u32[i] = ppu_vrsqrtefp(b_._u32[i], nj);
}
}
else
{
auto m = gv_bcst32(jm_mask, &ppu_thread::jm_mask);
auto b = ppu_flush_denormal<false, Flags...>(m, std::move(b_));
d = ppu_flush_denormal<true, Flags...>(std::move(m), ppu_set_vnan<Flags...>(gv_divfs(gv_bcstfs(1.0f), gv_sqrtfs(b)), b));
}
};
RETURN_(ppu.vr[op.vd], ppu.vr[op.vb], ppu.nj);
RETURN_(ppu.vr[op.vd], ppu.vr[op.vb], ppu.jm_mask, ppu.nj);
}
template <u32 Build, ppu_exec_bit... Flags>
@@ -7368,6 +7387,8 @@ ppu_interpreter_rt_base::ppu_interpreter_rt_base() noexcept
selected += fix_vnan;
if (g_cfg.core.ppu_set_fpcc)
selected += set_fpcc;
if (g_cfg.core.ppu_set_vest)
selected += set_vest;
if (g_cfg.core.use_accurate_dfma)
selected += use_dfma;
if (g_cfg.core.ppu_debug)
+3
View File
@@ -5569,6 +5569,7 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
accurate_nj_mode,
contains_symbol_resolver,
daz_and_ftz,
accurate_vest,
__bitset_enum_max
};
@@ -5599,6 +5600,8 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
settings += ppu_settings::contains_symbol_resolver; // Avoid invalidating all modules for this purpose
if (g_cfg.core.set_daz_and_ftz)
settings += ppu_settings::daz_and_ftz;
if (g_cfg.core.ppu_set_vest)
settings += ppu_settings::accurate_vest;
// Write version, hash, CPU, settings
fmt::append(obj_name, "v9-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu.to_string()));
+14 -2
View File
@@ -1780,7 +1780,13 @@ void PPUTranslator::VPKUWUS(ppu_opcode_t op)
void PPUTranslator::VREFP(ppu_opcode_t op)
{
SetVr(op.vd, VecEstimate("__vrefp", op.vb));
if (g_cfg.core.ppu_set_vest)
{
SetVr(op.vd, VecEstimate("__vrefp", op.vb));
return;
}
set_vr(op.vd, vec_handle_result(fsplat<f32[4]>(1.0) / get_vr<f32[4]>(op.vb)));
}
void PPUTranslator::VRFIM(ppu_opcode_t op)
@@ -1823,7 +1829,13 @@ void PPUTranslator::VRLW(ppu_opcode_t op)
void PPUTranslator::VRSQRTEFP(ppu_opcode_t op)
{
SetVr(op.vd, VecEstimate("__vrsqrtefp", op.vb));
if (g_cfg.core.ppu_set_vest)
{
SetVr(op.vd, VecEstimate("__vrsqrtefp", op.vb));
return;
}
set_vr(op.vd, vec_handle_result(fsplat<f32[4]>(1.0) / callf<f32[4]>(get_intrinsic<f32[4]>(Intrinsic::sqrt), get_vr<f32[4]>(op.vb))));
}
void PPUTranslator::VSEL(ppu_opcode_t op)
+1
View File
@@ -71,6 +71,7 @@ struct cfg_root : cfg::node
cfg::_bool ppu_use_nj_bit{ this, "PPU Accurate Non-Java Mode", false }; // Accuracy. If set, accurately emulate NJ flag. Implies NJ fixup.
cfg::_bool ppu_set_vnan{ this, "PPU Accurate Vector NaN Values", false }; // Accuracy. Implies ppu_fix_vnan.
cfg::_bool ppu_set_fpcc{ this, "PPU Set FPCC Bits", false }; // Accuracy.
cfg::_bool ppu_set_vest{ this, "PPU Accurate Vector Estimates", false }; // Accuracy. Hardware exact vrefp and vrsqrtefp.
cfg::_bool debug_console_mode{ this, "Debug Console Mode", false }; // Debug console emulation, not recommended
cfg::_bool hook_functions{ this, "Hook static functions" };
+1
View File
@@ -53,6 +53,7 @@ const std::map<emu_settings_type, cfg_location> settings_location =
{ emu_settings_type::AccuratePPUNJ, get_cfg_location(local_cfg.core.ppu_use_nj_bit) },
{ emu_settings_type::AccuratePPUVNAN, get_cfg_location(local_cfg.core.ppu_set_vnan) },
{ emu_settings_type::AccuratePPUFPCC, get_cfg_location(local_cfg.core.ppu_set_fpcc) },
{ emu_settings_type::AccuratePPUVEST, get_cfg_location(local_cfg.core.ppu_set_vest) },
{ emu_settings_type::MaxPreemptCount, get_cfg_location(local_cfg.core.max_cpu_preempt_count_per_frame) },
{ emu_settings_type::SPUProfiler, get_cfg_location(local_cfg.core.spu_prof) },
{ emu_settings_type::DisableSpinOptimization, get_cfg_location(local_cfg.core.spu_getllar_spin_optimization_disabled) },
+1
View File
@@ -47,6 +47,7 @@ enum class emu_settings_type
AccuratePPUNJ,
AccuratePPUVNAN,
AccuratePPUFPCC,
AccuratePPUVEST,
MaxPreemptCount,
SPUProfiler,
DisableSpinOptimization,
+1
View File
@@ -1442,6 +1442,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
// Checkboxes
EnhanceCheckBox(emu_settings_type::DebugConsoleMode, ui->debugConsoleMode, tooltips.settings.debug_console_mode);
EnhanceCheckBox(emu_settings_type::AccuratePPUVEST, ui->accuratePPUVEST, tooltips.settings.accurate_ppuvest);
EnhanceCheckBox(emu_settings_type::AccurateRSXAccess, ui->accurateRSXAccess, tooltips.settings.accurate_rsx_access);
EnhanceCheckBox(emu_settings_type::AccurateSpuDMA, ui->accurateSpuDMA, tooltips.settings.accurate_spu_dma);
EnhanceCheckBox(emu_settings_type::LLVMPrecompilation, ui->llvmPrecompilation, tooltips.settings.llvm_precompilation);
+7
View File
@@ -2538,6 +2538,13 @@
<string>Core</string>
</property>
<layout class="QVBoxLayout" name="gb_advanced_core_layout">
<item>
<widget class="QCheckBox" name="accuratePPUVEST">
<property name="text">
<string>Accurate PPU Vector Estimates</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="accurateRSXAccess">
<property name="text">
+1
View File
@@ -26,6 +26,7 @@ public:
const QString lib_default_lle = tr("Select to HLE. (LLE by default)");
const QString debug_console_mode = tr("Increases the amount of usable system memory to match a DECR console and more.\nCauses some software to behave differently than on retail hardware.");
const QString accurate_ppuvest = tr("Computes PPU vector reciprocal and reciprocal square root estimates exactly as the PS3 does.\nSome games compare these results with a real console, for example in online play. Comes at a performance cost.\nIf unsure, do not modify this setting.");
const QString accurate_rsx_access = tr("Forces RSX pauses on SPU MFC_GETLLAR and SPU MFC_PUTLLUC operations.");
const QString accurate_spu_dma = tr("Accurately processes SPU DMA operations.");
const QString silence_all_logs = tr("Stop writing any logs after game startup. Don't use unless you believe it's necessary.");