mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
Improve state load time (#995)
* load state speed improvements * more * more * more
This commit is contained in:
@@ -702,8 +702,9 @@ void rdp_check_framebuffers(uint32_t address, uint32_t length) {
|
||||
|
||||
size_t rdp_state_size() { return sizeof(RDP_DEVICE); }
|
||||
|
||||
void rdp_save_state(uint8_t *state) {
|
||||
processor->wait_for_timeline(processor->signal_timeline());
|
||||
void rdp_save_state(uint8_t *state, bool rewind) {
|
||||
if (!rewind) // speed hack for rewind
|
||||
processor->wait_for_timeline(processor->signal_timeline());
|
||||
memcpy(state, &rdp_device, sizeof(RDP_DEVICE));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ uint64_t rdp_process_commands();
|
||||
void rdp_onscreen_message(const char *message, MESSAGE_LENGTH milliseconds);
|
||||
void rdp_check_framebuffers(uint32_t address, uint32_t length);
|
||||
size_t rdp_state_size();
|
||||
void rdp_save_state(uint8_t *state);
|
||||
void rdp_save_state(uint8_t *state, bool rewind);
|
||||
void rdp_load_state(GFX_INFO _gfx_info, const uint8_t *state);
|
||||
void rdp_set_fps(uint32_t fps, uint32_t vis);
|
||||
|
||||
|
||||
+24
-26
@@ -255,32 +255,26 @@ pub fn zero_m128i() -> __m128i {
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn clone_without_ui(&self) -> Device {
|
||||
Device {
|
||||
netplay: None,
|
||||
ui: ui::Ui::default(),
|
||||
byte_swap: self.byte_swap,
|
||||
savestate: self.savestate.clone(),
|
||||
cpu: self.cpu.clone(),
|
||||
pif: self.pif.clone(),
|
||||
cart: self.cart.clone(),
|
||||
memory: self.memory.clone(),
|
||||
rsp: self.rsp.clone(),
|
||||
rdp: self.rdp.clone(),
|
||||
rdram: self.rdram.clone(),
|
||||
mi: self.mi.clone(),
|
||||
pi: self.pi.clone(),
|
||||
vi: self.vi.clone(),
|
||||
ai: self.ai.clone(),
|
||||
si: self.si.clone(),
|
||||
ri: self.ri.clone(),
|
||||
rng: self.rng.clone(),
|
||||
vru: self.vru.clone(),
|
||||
transferpaks: self.transferpaks.clone(),
|
||||
cheats: self.cheats.clone(),
|
||||
}
|
||||
pub fn clone_state(&mut self, device: &Device) {
|
||||
self.cpu.clone_from(&device.cpu);
|
||||
self.pif.clone_from(&device.pif);
|
||||
self.cart.clone_from(&device.cart);
|
||||
self.memory.clone_from(&device.memory);
|
||||
self.rsp.clone_from(&device.rsp);
|
||||
self.rdp.clone_from(&device.rdp);
|
||||
self.rdram.clone_from(&device.rdram);
|
||||
self.mi.clone_from(&device.mi);
|
||||
self.pi.clone_from(&device.pi);
|
||||
self.vi.clone_from(&device.vi);
|
||||
self.ai.clone_from(&device.ai);
|
||||
self.si.clone_from(&device.si);
|
||||
self.ri.clone_from(&device.ri);
|
||||
self.vru.clone_from(&device.vru);
|
||||
self.transferpaks.clone_from(&device.transferpaks);
|
||||
self.cheats.clone_from(&device.cheats);
|
||||
}
|
||||
pub fn new() -> Device {
|
||||
|
||||
pub fn new(with_ui: bool) -> Device {
|
||||
let mut byte_swap: usize = 0;
|
||||
let test: [u8; 4] = [1, 2, 3, 4];
|
||||
// if the host computer is little endian, that means the RDRAM will be stored as little endian
|
||||
@@ -290,7 +284,11 @@ impl Device {
|
||||
}
|
||||
Device {
|
||||
netplay: None,
|
||||
ui: ui::Ui::new(),
|
||||
ui: if with_ui {
|
||||
ui::Ui::new()
|
||||
} else {
|
||||
ui::Ui::default()
|
||||
},
|
||||
byte_swap,
|
||||
savestate: savestates::Savestate {
|
||||
save_state: false,
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ pub async fn run(args: Args, arg_count: usize) -> std::io::Result<()> {
|
||||
return Err(Error::other("Savestate slot must be between 0 and 9"));
|
||||
}
|
||||
|
||||
let mut device = device::Device::new();
|
||||
let mut device = device::Device::new(true);
|
||||
|
||||
device.ui.config.recent_roms.retain(|x| *x != game);
|
||||
device.ui.config.recent_roms.insert(0, game);
|
||||
|
||||
+78
-54
@@ -5,6 +5,7 @@ use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
|
||||
use serde::ser::{Serialize, SerializeSeq, Serializer};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
struct M128iArrayVisitor<const N: usize>;
|
||||
|
||||
@@ -81,35 +82,55 @@ pub struct Savestate {
|
||||
}
|
||||
|
||||
pub struct SavestateData {
|
||||
device_data: Vec<u8>,
|
||||
saves_data: Vec<u8>,
|
||||
device: device::Device,
|
||||
saves: ui::storage::Saves,
|
||||
rdp_state: Vec<u8>,
|
||||
ra_state: Vec<u8>,
|
||||
}
|
||||
|
||||
static DEVICE_CLONE: std::sync::LazyLock<std::sync::Mutex<device::Device>> =
|
||||
std::sync::LazyLock::new(|| std::sync::Mutex::new(device::Device::new(false)));
|
||||
|
||||
static SAVES_CLONE: std::sync::LazyLock<std::sync::Mutex<ui::storage::Saves>> =
|
||||
std::sync::LazyLock::new(|| std::sync::Mutex::new(ui::storage::Saves::default()));
|
||||
|
||||
pub fn create_savestate(device: &mut device::Device, rewind: bool) {
|
||||
if let Ok(mut device_clone) = DEVICE_CLONE.lock()
|
||||
&& let Ok(mut saves_clone) = SAVES_CLONE.lock()
|
||||
{
|
||||
device_clone.clone_state(device);
|
||||
saves_clone.clone_from(&device.ui.storage.saves);
|
||||
} else {
|
||||
ui::video::onscreen_message(
|
||||
"Failed to create savestate",
|
||||
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut rdp_state: Vec<u8> = vec![0; ui::video::state_size()];
|
||||
ui::video::save_state(rdp_state.as_mut_ptr());
|
||||
ui::video::save_state(rdp_state.as_mut_ptr(), rewind);
|
||||
|
||||
let mut ra_state: Vec<u8> = vec![0; retroachievements::state_size()];
|
||||
retroachievements::save_state(ra_state.as_mut_ptr(), ra_state.len());
|
||||
|
||||
let save_path = device.ui.storage.paths.savestate_file_path.clone();
|
||||
|
||||
let device_clone = device.clone_without_ui();
|
||||
let saves_clone = device.ui.storage.saves.clone();
|
||||
let save_state_slot = device.ui.storage.save_state_slot;
|
||||
let rewind_pool = device.savestate.rewind_pool.clone();
|
||||
tokio::spawn(async move {
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut error = false;
|
||||
if let Ok(device_data) = postcard::to_stdvec(&device_clone)
|
||||
&& let Ok(saves_data) = postcard::to_stdvec(&saves_clone)
|
||||
|
||||
if let Ok(mut device_clone) = DEVICE_CLONE.lock()
|
||||
&& let Ok(saves_clone) = SAVES_CLONE.lock()
|
||||
{
|
||||
if rewind {
|
||||
let mut state = device::Device::new(false);
|
||||
std::mem::swap(&mut state, &mut *device_clone);
|
||||
if let Ok(mut pool) = rewind_pool.lock() {
|
||||
pool.push_back(SavestateData {
|
||||
device_data,
|
||||
saves_data,
|
||||
device: state,
|
||||
saves: saves_clone.clone(),
|
||||
rdp_state,
|
||||
ra_state,
|
||||
});
|
||||
@@ -118,13 +139,16 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Ok(compressed_file) = ui::storage::compress_file(&[
|
||||
(&device_data, "device"),
|
||||
(&saves_data, "saves"),
|
||||
(&rdp_state, "rdp_state"),
|
||||
(&ra_state, "ra_state"),
|
||||
]) {
|
||||
if let Err(e) = tokio::fs::write(save_path, compressed_file).await {
|
||||
if let Ok(device_data) = postcard::to_stdvec(device_clone.deref())
|
||||
&& let Ok(saves_data) = postcard::to_stdvec(saves_clone.deref())
|
||||
&& let Ok(compressed_file) = ui::storage::compress_file(&[
|
||||
(&device_data, "device"),
|
||||
(&saves_data, "saves"),
|
||||
(&rdp_state, "rdp_state"),
|
||||
(&ra_state, "ra_state"),
|
||||
])
|
||||
{
|
||||
if let Err(e) = std::fs::write(save_path, compressed_file) {
|
||||
eprintln!("Error writing savestate: {}", e);
|
||||
error = true;
|
||||
}
|
||||
@@ -136,6 +160,7 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) {
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if error {
|
||||
ui::video::onscreen_message(
|
||||
&format!("Failed to create savestate in slot {}", save_state_slot),
|
||||
@@ -171,55 +196,54 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) {
|
||||
&& let Ok(saves_data) = ui::storage::decompress_file(savestate, "saves")
|
||||
&& let Ok(rdp_state) = ui::storage::decompress_file(savestate, "rdp_state")
|
||||
&& let Ok(ra_state) = ui::storage::decompress_file(savestate, "ra_state")
|
||||
&& let Ok(state) = postcard::from_bytes::<device::Device>(&device_data)
|
||||
&& let Ok(saves) = postcard::from_bytes(&saves_data)
|
||||
{
|
||||
Some(SavestateData {
|
||||
device_data,
|
||||
saves_data,
|
||||
device: state,
|
||||
saves,
|
||||
rdp_state,
|
||||
ra_state,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(state_data) = state_data
|
||||
&& let Ok(mut state) = postcard::from_bytes::<device::Device>(&state_data.device_data)
|
||||
&& let Ok(saves) = postcard::from_bytes(&state_data.saves_data)
|
||||
&& device.rdram.size == state.rdram.size
|
||||
|
||||
if let Some(mut state) = state_data
|
||||
&& device.rdram.size == state.device.rdram.size
|
||||
{
|
||||
device.savestate.last_rewind_saved = state.vi.elapsed_time;
|
||||
device.savestate.last_rewind_saved = state.device.vi.elapsed_time;
|
||||
|
||||
device.ui.storage.saves = saves;
|
||||
std::mem::swap(&mut device.ui.storage.saves, &mut state.saves);
|
||||
|
||||
std::mem::swap(&mut device.cpu, &mut state.cpu);
|
||||
std::mem::swap(&mut device.pif, &mut state.pif);
|
||||
std::mem::swap(&mut device.cpu, &mut state.device.cpu);
|
||||
std::mem::swap(&mut device.pif, &mut state.device.pif);
|
||||
|
||||
let rom = device.cart.rom.clone();
|
||||
std::mem::swap(&mut device.cart, &mut state.cart);
|
||||
device.cart.rom = rom;
|
||||
std::mem::swap(&mut device.cart, &mut state.device.cart);
|
||||
std::mem::swap(&mut device.cart.rom, &mut state.device.cart.rom); // ROM is not included in the savestate
|
||||
|
||||
std::mem::swap(&mut device.memory, &mut state.memory);
|
||||
std::mem::swap(&mut device.rsp, &mut state.rsp);
|
||||
std::mem::swap(&mut device.rdp, &mut state.rdp);
|
||||
std::mem::swap(&mut device.memory, &mut state.device.memory);
|
||||
std::mem::swap(&mut device.rsp, &mut state.device.rsp);
|
||||
std::mem::swap(&mut device.rdp, &mut state.device.rdp);
|
||||
|
||||
device.rdram.mem.clone_from(&state.rdram.mem);
|
||||
device.rdram.regs = state.rdram.regs;
|
||||
device.rdram.mem.clone_from(&state.device.rdram.mem); // RDRAM address should not change
|
||||
std::mem::swap(&mut device.rdram.regs, &mut state.device.rdram.regs);
|
||||
|
||||
std::mem::swap(&mut device.mi, &mut state.mi);
|
||||
std::mem::swap(&mut device.pi, &mut state.pi);
|
||||
std::mem::swap(&mut device.vi, &mut state.vi);
|
||||
std::mem::swap(&mut device.ai, &mut state.ai);
|
||||
std::mem::swap(&mut device.si, &mut state.si);
|
||||
std::mem::swap(&mut device.ri, &mut state.ri);
|
||||
std::mem::swap(&mut device.vru, &mut state.vru);
|
||||
std::mem::swap(&mut device.cheats, &mut state.cheats);
|
||||
std::mem::swap(&mut device.mi, &mut state.device.mi);
|
||||
std::mem::swap(&mut device.pi, &mut state.device.pi);
|
||||
std::mem::swap(&mut device.vi, &mut state.device.vi);
|
||||
std::mem::swap(&mut device.ai, &mut state.device.ai);
|
||||
std::mem::swap(&mut device.si, &mut state.device.si);
|
||||
std::mem::swap(&mut device.ri, &mut state.device.ri);
|
||||
std::mem::swap(&mut device.vru, &mut state.device.vru);
|
||||
std::mem::swap(&mut device.cheats, &mut state.device.cheats);
|
||||
|
||||
let mut tpak_rom = [vec![], vec![], vec![], vec![]];
|
||||
for (i, item) in tpak_rom.iter_mut().enumerate() {
|
||||
*item = device.transferpaks[i].cart.rom.clone();
|
||||
}
|
||||
std::mem::swap(&mut device.transferpaks, &mut state.transferpaks);
|
||||
for (i, item) in tpak_rom.iter().enumerate() {
|
||||
device.transferpaks[i].cart.rom = item.clone();
|
||||
std::mem::swap(&mut device.transferpaks, &mut state.device.transferpaks);
|
||||
for (i, item) in device.transferpaks.iter_mut().enumerate() {
|
||||
std::mem::swap(
|
||||
&mut item.cart.rom,
|
||||
&mut state.device.transferpaks[i].cart.rom,
|
||||
); // ROM is not included in the savestate
|
||||
}
|
||||
|
||||
device::memory::init(device);
|
||||
@@ -276,11 +300,11 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) {
|
||||
}
|
||||
|
||||
ui::audio::update_freq(device);
|
||||
ui::video::load_state(device, state_data.rdp_state.as_ptr());
|
||||
ui::video::load_state(device, state.rdp_state.as_ptr());
|
||||
|
||||
retroachievements::set_rdram(device.rdram.mem.as_ptr(), device.rdram.size as usize);
|
||||
if !state_data.ra_state.is_empty() {
|
||||
retroachievements::load_state(state_data.ra_state.as_ptr(), state_data.ra_state.len());
|
||||
if !state.ra_state.is_empty() {
|
||||
retroachievements::load_state(state.ra_state.as_ptr(), state.ra_state.len());
|
||||
} else {
|
||||
retroachievements::load_state(std::ptr::null(), 0);
|
||||
}
|
||||
|
||||
+2
-2
@@ -161,8 +161,8 @@ pub fn state_size() -> usize {
|
||||
unsafe { rdp_state_size() }
|
||||
}
|
||||
|
||||
pub fn save_state(rdp_state: *mut u8) {
|
||||
unsafe { rdp_save_state(rdp_state) }
|
||||
pub fn save_state(rdp_state: *mut u8, rewind: bool) {
|
||||
unsafe { rdp_save_state(rdp_state, rewind) }
|
||||
}
|
||||
|
||||
pub fn load_state(device: &mut device::Device, rdp_state: *const u8) {
|
||||
|
||||
Reference in New Issue
Block a user