Don't store luts in savestate (#993)

* use hashmap for tlb

* more

* more

* more

* more

* more
This commit is contained in:
Logan McNaughton 2026-05-31 08:37:11 +02:00 committed by GitHub
parent 79d2c023e6
commit 5c7170268e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 63 additions and 50 deletions

1
Cargo.lock generated
View File

@ -2232,6 +2232,7 @@ dependencies = [
"regex",
"reqwest",
"rfd",
"rustc-hash 2.1.2",
"sdl3-image-sys",
"sdl3-src",
"sdl3-sys",

View File

@ -25,6 +25,7 @@ encoding_rs = "0.8"
mimalloc = "0.1"
regex = "1.11"
tokio-tungstenite = "0.29"
rustc-hash = "2.1"
futures = "0.3"
socket2 = { version = "0.6", features = ["all"] }
rand = "0.10"

View File

@ -26,7 +26,7 @@ pub type Cheat = std::collections::BTreeMap<String, std::collections::BTreeMap<S
pub fn init(
device: &mut device::Device,
cheat_settings: std::collections::HashMap<String, Option<String>>,
cheat_settings: rustc_hash::FxHashMap<String, Option<String>>,
) {
let cheats = serde_json::from_slice::<Cheat>(include_bytes!("../data/cheats.json"))
.unwrap()

View File

@ -16,7 +16,7 @@ const N64_EXTENSIONS_UNCOMPRESSED: [&str; 8] =
use rand::{Rng, SeedableRng};
use crate::{cheats, netplay, retroachievements, savestates, ui};
use std::{collections::HashMap, io::Read};
use std::io::Read;
pub mod ai;
pub mod cache;
@ -310,20 +310,8 @@ impl Device {
is_event: false,
instrs: [cop0::reserved; 32],
instrs2: [cop0::reserved; 32],
tlb_lut_w: vec![
tlb::TlbLut {
address: 0,
cached: false,
};
0x100000
],
tlb_lut_r: vec![
tlb::TlbLut {
address: 0,
cached: false,
};
0x100000
],
tlb_lut_w: vec![],
tlb_lut_r: vec![],
tlb_entries: [tlb::TlbEntry {
mask: 0,
vpn2: 0,
@ -558,7 +546,7 @@ impl Device {
word_buffer: [0; 40],
words: Vec::new(),
talking: false,
word_mappings: HashMap::new(),
word_mappings: rustc_hash::FxHashMap::default(),
},
rng: set_rng(),
transferpaks: [

View File

@ -1,7 +1,6 @@
use crate::device;
#[cfg(all(feature = "gui", not(target_os = "android")))]
use crate::ui;
use std::collections::HashMap;
const JCMD_VRU_READ: u8 = 0x09;
const JCMD_VRU_WRITE: u8 = 0x0A;
@ -28,7 +27,7 @@ pub struct Vru {
pub word_buffer: [u16; 40],
pub words: Vec<String>,
pub talking: bool,
pub word_mappings: HashMap<String, String>,
pub word_mappings: rustc_hash::FxHashMap<String, String>,
}
pub fn init(device: &mut device::Device) {
@ -186,7 +185,7 @@ pub fn process(device: &mut device::Device, channel: usize) {
#[cfg(any(not(feature = "gui"), target_os = "android"))]
let index = 0x7FFF;
let num_results = if index == 0x7FFF { 0 } else { 1 };
let data: HashMap<usize, u16> = HashMap::from([
let data: rustc_hash::FxHashMap<usize, u16> = rustc_hash::FxHashMap::from_iter([
(0, 0x8000),
(2, 0x0F00),
(4, 0), // error flags
@ -247,7 +246,7 @@ pub fn vru_talking_event(device: &mut device::Device) {
}
fn create_word_mappings(device: &mut device::Device) {
device.vru.word_mappings = HashMap::from([
device.vru.word_mappings = rustc_hash::FxHashMap::from_iter([
(
String::from("03A50024000303CF00A80003036000EA"),
String::from("pikachu"),

View File

@ -100,7 +100,9 @@ pub struct Cop0 {
pub instrs: [fn(&mut device::Device, u32); 32],
#[serde(skip, default = "savestates::default_instructions")]
pub instrs2: [fn(&mut device::Device, u32); 32],
#[serde(skip)]
pub tlb_lut_r: Vec<device::tlb::TlbLut>,
#[serde(skip)]
pub tlb_lut_w: Vec<device::tlb::TlbLut>,
pub tlb_entries: [device::tlb::TlbEntry; 32],
pub is_event: bool,
@ -369,6 +371,7 @@ pub fn init(device: &mut device::Device) {
];
map_instructions(device);
device::tlb::init(device);
// taken from VR4300 manual
device.cpu.cop0.regs[COP0_RANDOM_REG] = 0b00000000000000000000000000011111;

View File

@ -30,6 +30,23 @@ pub struct TlbEntry {
pub phys_odd: u64,
}
pub fn init(device: &mut device::Device) {
device.cpu.cop0.tlb_lut_r.resize(
0x100000,
device::tlb::TlbLut {
address: 0,
cached: false,
},
);
device.cpu.cop0.tlb_lut_w.resize(
0x100000,
device::tlb::TlbLut {
address: 0,
cached: false,
},
);
}
pub fn read(device: &mut device::Device, index: u64) {
if index > 31 {
return;
@ -181,7 +198,7 @@ fn tlb_unmap(device: &mut device::Device, index: u64) {
}
}
fn tlb_map(device: &mut device::Device, index: u64) {
pub fn tlb_map(device: &mut device::Device, index: u64) {
let e = &device.cpu.cop0.tlb_entries[index as usize];
if e.v_even != 0

View File

@ -38,7 +38,7 @@ pub struct PlayerData {
lag: u8,
count: u32,
pub reg_id: u32,
input_events: std::collections::HashMap<u32, InputEvent>,
input_events: rustc_hash::FxHashMap<u32, InputEvent>,
}
struct InputEvent {
@ -310,25 +310,25 @@ pub fn init(peer_addr: std::net::SocketAddr, player_number: u8) -> Netplay {
lag: 0,
count: 0,
reg_id: reg_id[0],
input_events: std::collections::HashMap::new(),
input_events: rustc_hash::FxHashMap::default(),
},
PlayerData {
lag: 0,
count: 0,
reg_id: reg_id[1],
input_events: std::collections::HashMap::new(),
input_events: rustc_hash::FxHashMap::default(),
},
PlayerData {
lag: 0,
count: 0,
reg_id: reg_id[2],
input_events: std::collections::HashMap::new(),
input_events: rustc_hash::FxHashMap::default(),
},
PlayerData {
lag: 0,
count: 0,
reg_id: reg_id[3],
input_events: std::collections::HashMap::new(),
input_events: rustc_hash::FxHashMap::default(),
},
],
}

View File

@ -97,12 +97,13 @@ pub fn create_savestate(device: &mut device::Device, rewind: bool) {
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 {
let mut error = false;
if let Ok(device_data) = postcard::to_stdvec(&device_clone)
&& let Ok(saves_data) = postcard::to_stdvec(&device_clone.ui.storage.saves)
&& let Ok(saves_data) = postcard::to_stdvec(&saves_clone)
{
if rewind {
if let Ok(mut pool) = rewind_pool.lock() {
@ -246,6 +247,11 @@ pub fn load_savestate(device: &mut device::Device, rewind: bool) {
}
}
device::tlb::init(device);
for i in 0..32 {
device::tlb::tlb_map(device, i);
}
device::pif::connect_pif_channels(device);
for i in 0..4 {
if let Some(handler) = device.pif.channels[i].pak_handler {

View File

@ -85,7 +85,7 @@ pub struct Usb {
pub struct GameSettings {
pub overclock: bool,
pub disable_expansion_pak: bool,
pub cheats: std::collections::HashMap<String, Option<String>>,
pub cheats: rustc_hash::FxHashMap<String, Option<String>>,
pub load_savestate_slot: Option<u32>,
}
@ -259,7 +259,7 @@ impl Ui {
write_pending: false,
},
romsave: storage::RomSave {
data: std::collections::HashMap::new(),
data: rustc_hash::FxHashMap::default(),
write_pending: false,
},
},

View File

@ -66,8 +66,7 @@ pub struct Emulation {
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Cheats {
pub cheats:
std::collections::HashMap<String, std::collections::HashMap<String, Option<String>>>,
pub cheats: rustc_hash::FxHashMap<String, rustc_hash::FxHashMap<String, Option<String>>>,
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
@ -109,7 +108,7 @@ impl Cheats {
}
}
Cheats {
cheats: std::collections::HashMap::new(),
cheats: rustc_hash::FxHashMap::default(),
}
}
}

View File

@ -72,7 +72,7 @@ fn run_with_path(weak: slint::Weak<AppWindow>, path: std::path::PathBuf) {
ui::GameSettings {
overclock: handle.get_overclock_n64_cpu(),
disable_expansion_pak: handle.get_disable_expansion_pak(),
cheats: std::collections::HashMap::new(), // will be filled in later
cheats: rustc_hash::FxHashMap::default(), // will be filled in later
load_savestate_slot: None,
},
None,
@ -702,7 +702,7 @@ fn open_rom(app: &AppWindow) {
ui::GameSettings {
overclock,
disable_expansion_pak,
cheats: std::collections::HashMap::new(), // will be filled in later
cheats: rustc_hash::FxHashMap::default(), // will be filled in later
load_savestate_slot: None,
},
None,

View File

@ -19,7 +19,7 @@ pub struct NetplayRoom {
md5: Option<String>,
game_name: Option<String>,
pub port: Option<i32>,
features: Option<std::collections::HashMap<String, String>>,
features: Option<rustc_hash::FxHashMap<String, String>>,
buffer_target: Option<i32>,
}
@ -60,9 +60,8 @@ async fn get_local_servers() -> Vec<(String, String)> {
broadcast_sock.recv(&mut buffer),
)
.await
&& let Ok(data) = serde_json::from_slice::<std::collections::HashMap<String, String>>(
&buffer[..result],
)
&& let Ok(data) =
serde_json::from_slice::<rustc_hash::FxHashMap<String, String>>(&buffer[..result])
{
let mut local_servers = vec![];
for server in data.iter() {
@ -207,7 +206,7 @@ fn setup_create_window(
if let Ok(response) = response
&& let Ok(server) = response
.json::<std::collections::HashMap<String, String>>()
.json::<rustc_hash::FxHashMap<String, String>>()
.await
{
let server_url = server.values().next().unwrap();
@ -427,19 +426,19 @@ fn update_sessions(weak: slint::Weak<AppWindow>) {
.header("netplay-id", env!("NETPLAY_ID"))
.send();
tokio::spawn(async move {
let mut dispatcher_servers = std::collections::HashMap::new();
let mut dispatcher_servers = rustc_hash::FxHashMap::default();
let response = task.await;
if let Ok(response) = response
&& let Ok(servers) = response
.json::<std::collections::HashMap<String, String>>()
.json::<rustc_hash::FxHashMap<String, String>>()
.await
{
dispatcher_servers = servers;
}
let weak2 = weak.clone();
weak.upgrade_in_event_loop(move |handle| {
let mut servers: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
let mut servers: rustc_hash::FxHashMap<String, String> =
rustc_hash::FxHashMap::default();
let server_names = handle.get_netplay_server_names();
let server_urls = handle.get_netplay_server_urls();
for (i, server_name) in server_names.iter().enumerate() {
@ -529,7 +528,7 @@ fn create_session(
.as_millis()
.to_string();
let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME);
let mut features = std::collections::HashMap::new();
let mut features = rustc_hash::FxHashMap::default();
if !game_cheats.is_empty() {
features.insert("cheats".to_string(), game_cheats);
@ -1126,7 +1125,7 @@ pub fn netplay_window(app: &AppWindow) {
ui::GameSettings {
overclock: handle.get_overclock_n64_cpu(),
disable_expansion_pak: handle.get_disable_expansion_pak(),
cheats: std::collections::HashMap::new(), // not used here
cheats: rustc_hash::FxHashMap::default(), // not used here
load_savestate_slot: None,
},
write_sender,

View File

@ -26,19 +26,19 @@ pub struct Paths {
// the bool indicates whether the save has been written to
// if that is the case, it will be flushed to the disk when the program closes
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct Save {
pub data: Vec<u8>,
pub write_pending: bool,
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct RomSave {
pub data: std::collections::HashMap<u32, u8>,
pub data: rustc_hash::FxHashMap<u32, u8>,
pub write_pending: bool,
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct Saves {
pub eeprom: Save,
pub sram: Save,