mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-26 04:32:35 +01:00
Savestates/SPU: Kill emulation when its safe to save SPU state
This commit is contained in:
parent
105781fa76
commit
3e51426379
@ -1196,3 +1196,72 @@ u32 CPUDisAsm::DisAsmBranchTarget(s32 /*imm*/)
|
|||||||
// Unused
|
// Unused
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates()
|
||||||
|
{
|
||||||
|
const u64 start = get_system_time();
|
||||||
|
|
||||||
|
// Attempt to lock for half a second, if somehow takes longer abort it
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (cpu_thread::suspend_all(nullptr, {}, []()
|
||||||
|
{
|
||||||
|
return idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||||
|
{
|
||||||
|
if (!spu.unsavable)
|
||||||
|
{
|
||||||
|
if (Emu.IsPaused())
|
||||||
|
{
|
||||||
|
// If emulation is paused, we can only hope it's already in a state compatible with savestates
|
||||||
|
return !!(spu.state & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ensure(!spu.state.test_and_set(cpu_flag::dbg_global_pause));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}).ret;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
if (Emu.IsPaused())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's faster to lock once
|
||||||
|
reader_lock lock(id_manager::g_mutex);
|
||||||
|
|
||||||
|
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||||
|
{
|
||||||
|
spu.state -= cpu_flag::dbg_global_pause;
|
||||||
|
}, idm::unlocked);
|
||||||
|
|
||||||
|
// For faster signalling, first remove state flags then batch notifications
|
||||||
|
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||||
|
{
|
||||||
|
if (spu.state & cpu_flag::wait)
|
||||||
|
{
|
||||||
|
spu.state.notify_one();
|
||||||
|
}
|
||||||
|
}, idm::unlocked);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} while (std::this_thread::yield(), get_system_time() - start <= 500'000);
|
||||||
|
|
||||||
|
idm::select<named_thread<spu_thread>>([&](u32, named_thread<spu_thread>& spu)
|
||||||
|
{
|
||||||
|
if (spu.state.test_and_reset(cpu_flag::dbg_global_pause))
|
||||||
|
{
|
||||||
|
spu.state.notify_one();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "lv2/sys_prx.h"
|
#include "lv2/sys_prx.h"
|
||||||
#include "lv2/sys_overlay.h"
|
#include "lv2/sys_overlay.h"
|
||||||
#include "lv2/sys_process.h"
|
#include "lv2/sys_process.h"
|
||||||
|
#include "lv2/sys_spu.h"
|
||||||
|
|
||||||
#ifdef LLVM_AVAILABLE
|
#ifdef LLVM_AVAILABLE
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -1400,6 +1401,21 @@ void ppu_thread::cpu_task()
|
|||||||
// (the farther it's postponed, the less accuracy of guest time has been lost)
|
// (the farther it's postponed, the less accuracy of guest time has been lost)
|
||||||
Emu.FixGuestTime();
|
Emu.FixGuestTime();
|
||||||
|
|
||||||
|
// Run SPUs waiting on a syscall (savestates related)
|
||||||
|
idm::select<named_thread<spu_thread>>([&](u32, named_thread<spu_thread>& spu)
|
||||||
|
{
|
||||||
|
if (spu.group && spu.index == spu.group->waiter_spu_index)
|
||||||
|
{
|
||||||
|
if (std::exchange(spu.stop_flag_removal_protection, false))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(spu.state.test_and_reset(cpu_flag::stop));
|
||||||
|
spu.state.notify_one(cpu_flag::stop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Check if this is the only PPU left to initialize (savestates related)
|
// Check if this is the only PPU left to initialize (savestates related)
|
||||||
if (lv2_obj::is_scheduler_ready())
|
if (lv2_obj::is_scheduler_ready())
|
||||||
{
|
{
|
||||||
|
@ -4310,7 +4310,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call cpu_thread::check_state if necessary and return or continue (full check)
|
// Call cpu_thread::check_state if necessary and return or continue (full check)
|
||||||
void check_state(u32 addr)
|
void check_state(u32 addr, bool may_be_unsafe_for_savestate = true)
|
||||||
{
|
{
|
||||||
const auto pstate = spu_ptr<u32>(&spu_thread::state);
|
const auto pstate = spu_ptr<u32>(&spu_thread::state);
|
||||||
const auto _body = llvm::BasicBlock::Create(m_context, "", m_function);
|
const auto _body = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
@ -4318,7 +4318,24 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
|||||||
m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely);
|
m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely);
|
||||||
m_ir->SetInsertPoint(check);
|
m_ir->SetInsertPoint(check);
|
||||||
update_pc(addr);
|
update_pc(addr);
|
||||||
|
|
||||||
|
if (may_be_unsafe_for_savestate && std::none_of(std::begin(m_block->phi), std::end(m_block->phi), FN(!!x)))
|
||||||
|
{
|
||||||
|
may_be_unsafe_for_savestate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (may_be_unsafe_for_savestate)
|
||||||
|
{
|
||||||
|
m_ir->CreateStore(m_ir->getInt8(1), spu_ptr<u8>(&spu_thread::unsavable), true);
|
||||||
|
}
|
||||||
|
|
||||||
m_ir->CreateStore(m_ir->getFalse(), m_fake_global1, true);
|
m_ir->CreateStore(m_ir->getFalse(), m_fake_global1, true);
|
||||||
|
|
||||||
|
if (may_be_unsafe_for_savestate)
|
||||||
|
{
|
||||||
|
m_ir->CreateStore(m_ir->getInt8(0), spu_ptr<u8>(&spu_thread::unsavable), true);
|
||||||
|
}
|
||||||
|
|
||||||
m_ir->CreateBr(_body);
|
m_ir->CreateBr(_body);
|
||||||
m_ir->SetInsertPoint(_body);
|
m_ir->SetInsertPoint(_body);
|
||||||
}
|
}
|
||||||
|
@ -1468,6 +1468,8 @@ void spu_thread::cpu_task()
|
|||||||
spu_runtime::g_gateway(*this, _ptr<u8>(0), nullptr);
|
spu_runtime::g_gateway(*this, _ptr<u8>(0), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsavable = false;
|
||||||
|
|
||||||
// Print some stats
|
// Print some stats
|
||||||
spu_log.notice("Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure);
|
spu_log.notice("Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure);
|
||||||
}
|
}
|
||||||
|
@ -847,6 +847,7 @@ public:
|
|||||||
|
|
||||||
const char* current_func{}; // Current STOP or RDCH blocking function
|
const char* current_func{}; // Current STOP or RDCH blocking function
|
||||||
u64 start_time{}; // Starting time of STOP or RDCH bloking function
|
u64 start_time{}; // Starting time of STOP or RDCH bloking function
|
||||||
|
bool unsavable = false; // Flag indicating whether saving the spu thread state is currently unsafe
|
||||||
|
|
||||||
atomic_t<u8> debugger_float_mode = 0;
|
atomic_t<u8> debugger_float_mode = 0;
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ bool serialize<rsx::rsx_state>(utils::serial& ar, rsx::rsx_state& o)
|
|||||||
{
|
{
|
||||||
ar(o.transform_program);
|
ar(o.transform_program);
|
||||||
|
|
||||||
if (GET_SERIALIZATION_VERSION(global_version))
|
// Hack for compatiblity with previous RSX captures
|
||||||
|
if (rsx::get_current_renderer()->state & cpu_flag::exit || GET_SERIALIZATION_VERSION(global_version))
|
||||||
{
|
{
|
||||||
ar(o.transform_constants);
|
ar(o.transform_constants);
|
||||||
}
|
}
|
||||||
|
@ -1878,22 +1878,6 @@ void Emulator::RunPPU()
|
|||||||
signalled_thread = true;
|
signalled_thread = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run SPUs waiting on a syscall (savestates related)
|
|
||||||
idm::select<named_thread<spu_thread>>([&](u32, named_thread<spu_thread>& spu)
|
|
||||||
{
|
|
||||||
if (spu.group && spu.index == spu.group->waiter_spu_index)
|
|
||||||
{
|
|
||||||
if (std::exchange(spu.stop_flag_removal_protection, false))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure(spu.state.test_and_reset(cpu_flag::stop));
|
|
||||||
spu.state.notify_one(cpu_flag::stop);
|
|
||||||
signalled_thread = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!signalled_thread)
|
if (!signalled_thread)
|
||||||
{
|
{
|
||||||
FixGuestTime();
|
FixGuestTime();
|
||||||
@ -2169,9 +2153,15 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern bool try_lock_vdec_context_creation();
|
extern bool try_lock_vdec_context_creation();
|
||||||
|
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates();
|
||||||
|
|
||||||
void Emulator::Kill(bool allow_autoexit, bool savestate)
|
void Emulator::Kill(bool allow_autoexit, bool savestate)
|
||||||
{
|
{
|
||||||
|
if (savestate && !try_lock_spu_threads_in_a_state_compatible_with_savestates())
|
||||||
|
{
|
||||||
|
sys_log.error("Failed to savestate: failed to lock SPU threads execution.");
|
||||||
|
}
|
||||||
|
|
||||||
if (savestate && !try_lock_vdec_context_creation())
|
if (savestate && !try_lock_vdec_context_creation())
|
||||||
{
|
{
|
||||||
sys_log.error("Failed to savestate: HLE VDEC (video decoder) context(s) exist."
|
sys_log.error("Failed to savestate: HLE VDEC (video decoder) context(s) exist."
|
||||||
|
Loading…
Reference in New Issue
Block a user