Skip to content
This repository has been archived by the owner on Aug 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #37 from Dr-Emann/libretro
Browse files Browse the repository at this point in the history
Massive save state speedup
  • Loading branch information
yupferris committed Nov 22, 2018
2 parents 8acf06d + 13d084e commit 0b6b679
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 16 deletions.
33 changes: 28 additions & 5 deletions rustual-boy-libretro/src/lib.rs 100644 → 100755
Expand Up @@ -60,6 +60,7 @@ pub struct Context {
system: Option<System>,
video_output_frame_buffer: OutputBuffer,
audio_frame_buffer: Vec<AudioFrame>,
last_serialized_state: Option<Vec<u8>>,
}

impl Context {
Expand All @@ -68,10 +69,12 @@ impl Context {
system: None,
video_output_frame_buffer: OutputBuffer::Xrgb1555(vec![0; DISPLAY_PIXELS as usize]),
audio_frame_buffer: vec![(0, 0); (SAMPLE_RATE as usize) / 50 * 2], // double space needed for 1 frame for lots of skid room
last_serialized_state: None,
}
}

fn load_game(&mut self, game_info: &GameInfo) -> bool {
self.last_serialized_state = None;
unsafe {
// It seems retroarch (and possibly other frontends) is a bit finicky with accepting the set pixel format
// callback, so we should call it a few times before giving up. We don't want to loop forever here though,
Expand Down Expand Up @@ -110,6 +113,7 @@ impl Context {
}

fn unload_game(&mut self) {
self.last_serialized_state = None;
self.system = None;
}

Expand All @@ -132,12 +136,14 @@ impl Context {
}

fn reset(&mut self) {
self.last_serialized_state = None;
// Pull out rom/sram from existing system, and build new system around them
let (rom, sram) = self.system.as_ref().map(|system| (system.virtual_boy.interconnect.rom.clone(), system.virtual_boy.interconnect.sram.clone())).unwrap();
self.system = Some(System::new(rom, sram));
}

fn run_frame(&mut self) {
self.last_serialized_state = None;
unsafe {
CALLBACKS.input_poll();

Expand Down Expand Up @@ -219,11 +225,19 @@ impl Context {
}
}

fn get_serialized_size(&self) -> size_t {
fn get_serialized_size(&mut self) -> size_t {
if let Some(ref serialized_bytes) = self.last_serialized_state {
return serialized_bytes.len();
}

if let Some(ref system) = self.system {
let state = get_state(&system.virtual_boy);
match serialize(state) {
Ok(serialized_bytes) => serialized_bytes.len() as _,
Ok(serialized_bytes) => {
let len = serialized_bytes.len();
self.last_serialized_state = Some(serialized_bytes);
len
}
Err(err) => {
// TODO: Remove this..?
println!("****** RUH ROH: Serialization failed for some reason :((( {}", err);
Expand All @@ -236,13 +250,21 @@ impl Context {
}
}

fn serialize(&self, data: *mut c_void, size: size_t) -> bool {
fn serialize(&mut self, data: *mut c_void, size: size_t) -> bool {
if let Some(ref serialized_bytes) = self.last_serialized_state {
let result = serialized_bytes.len() <= size;
if result {
unsafe { ptr::copy_nonoverlapping(serialized_bytes.as_ptr(), data as *mut u8, serialized_bytes.len()) };
}
return result;
}
if let Some(ref system) = self.system {
let state = get_state(&system.virtual_boy);
match serialize(state) {
Ok(serialized_bytes) => {
if serialized_bytes.len() == size {
unsafe { ptr::copy_nonoverlapping(serialized_bytes.as_ptr(), data as *mut u8, size) };
if serialized_bytes.len() <= size {
unsafe { ptr::copy_nonoverlapping(serialized_bytes.as_ptr(), data as *mut u8, serialized_bytes.len()) };
self.last_serialized_state = Some(serialized_bytes);

true
} else {
Expand All @@ -262,6 +284,7 @@ impl Context {
}

fn unserialize(&mut self, data: *const c_void, size: size_t) -> bool {
self.last_serialized_state = None;
let data_slice = unsafe { slice::from_raw_parts(data as *const u8, size) };
match deserialize(data_slice) {
Ok(deserialized_state) => {
Expand Down
10 changes: 10 additions & 0 deletions rustual-boy-serialization/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rustual-boy-serialization/Cargo.toml
Expand Up @@ -7,5 +7,6 @@ authors = ["ferris <yupferris@gmail.com>"]
rustual-boy-core = { path="../rustual-boy-core" }
serde = "1.0"
serde_derive = "1.0"
serde_bytes="0.10"
bincode = "1.0"
lz4 = "*"
22 changes: 11 additions & 11 deletions rustual-boy-serialization/src/lib.rs 100644 → 100755
Expand Up @@ -4,16 +4,16 @@ extern crate rustual_boy_core;
extern crate serde_derive;

extern crate serde;
extern crate serde_bytes;
extern crate bincode;

extern crate lz4;

pub mod version1;
mod serde_ibytes;

use lz4::{EncoderBuilder, Decoder};

use std::io::{copy, Cursor};

use rustual_boy_core::rom::*;
use rustual_boy_core::timer::*;
use rustual_boy_core::vip::*;
Expand All @@ -36,11 +36,13 @@ pub enum ApplyError {

pub fn serialize(state: State) -> Result<Vec<u8>, String> {
let versioned_state = VersionedState::Version1(state);
let bincode = bincode::serialize(&versioned_state).map_err(|e| format!("Couldn't serialize bincode: {}", e))?;
let mut cursor = Cursor::new(bincode);
let encoded = Vec::new();
let mut encoder = EncoderBuilder::new().build(encoded).map_err(|e| format!("Couldn't build lz4 encoder: {}", e))?;
copy(&mut cursor, &mut encoder).map_err(|e| format!("Couldn't encode lz4: {}", e))?;
let encoded = Vec::with_capacity(512 * 1024);
let mut encoder = EncoderBuilder::new()
.level(2)
.block_size(lz4::BlockSize::Max1MB)
.build(encoded)
.map_err(|e| format!("Couldn't build lz4 encoder: {}", e))?;
bincode::serialize_into(&mut encoder, &versioned_state).map_err(|e| format!("Couldn't save state: {}", e))?;
let (encoded, result) = encoder.finish();
if let Err(e) = result {
return Err(format!("Couldn't finish lz4 encoding: {}", e));
Expand All @@ -49,10 +51,8 @@ pub fn serialize(state: State) -> Result<Vec<u8>, String> {
}

pub fn deserialize(encoded: &[u8]) -> Result<State, String> {
let mut decoder = Decoder::new(encoded).map_err(|e| format!("Couldn't build lz4 decoder: {}", e))?;
let mut bincode = Vec::new();
copy(&mut decoder, &mut bincode).map_err(|e| format!("Couldn't decode lz4: {}", e))?;
let versioned_state = bincode::deserialize(&bincode).map_err(|e| format!("Couldn't deserialize bincode: {}", e))?;
let decoder = Decoder::new(encoded).map_err(|e| format!("Couldn't build lz4 decoder: {}", e))?;
let versioned_state = bincode::deserialize_from(decoder).map_err(|e| format!("Couldn't deserialize state: {}", e))?;
Ok(match versioned_state {
VersionedState::Version1(state) => state,
})
Expand Down
31 changes: 31 additions & 0 deletions rustual-boy-serialization/src/serde_ibytes.rs
@@ -0,0 +1,31 @@
use std::{mem, slice};
use serde_bytes::ByteBuf;
use serde::{Serializer, Deserializer, Deserialize};

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where T: From<Vec<i8>>,
D: Deserializer<'de>
{
ByteBuf::deserialize(deserializer).map(|buf| {
let mut u8_v: Vec<u8> = buf.into();

let i8_v = unsafe {
Vec::from_raw_parts(
u8_v.as_mut_ptr() as *mut i8,
u8_v.len(),
u8_v.capacity()
)
};
mem::forget(u8_v);
i8_v.into()
})
}

pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
where T: ?Sized + AsRef<[i8]>,
S: Serializer
{
let i8_s: &[i8] = bytes.as_ref();
let u8_s: &[u8] = unsafe { slice::from_raw_parts(i8_s.as_ptr() as *const u8, i8_s.len()) };
serializer.serialize_bytes(u8_s)
}
8 changes: 8 additions & 0 deletions rustual-boy-serialization/src/version1.rs 100644 → 100755
@@ -1,4 +1,6 @@
use std::collections::HashSet;
use serde_bytes;
use serde_ibytes;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct State {
Expand All @@ -8,7 +10,9 @@ pub struct State {

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct InterconnectState {
#[serde(with = "serde_bytes")]
pub rom: Box<[u8]>,
#[serde(with = "serde_bytes")]
pub wram: Box<[u8]>,
pub sram: SramState,
pub vip: VipState,
Expand Down Expand Up @@ -48,6 +52,7 @@ pub struct GamePadState {

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct SramState {
#[serde(with = "serde_bytes")]
pub bytes: Box<[u8]>,

pub size: usize,
Expand Down Expand Up @@ -88,6 +93,7 @@ pub enum DrawingStateState {

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct VipState {
#[serde(with = "serde_bytes")]
pub vram: Box<[u8]>,

pub display_state: DisplayStateState,
Expand Down Expand Up @@ -250,7 +256,9 @@ pub struct NoiseSoundState {

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct VsuState {
#[serde(with = "serde_bytes")]
pub waveform_data: Box<[u8]>,
#[serde(with = "serde_ibytes")]
pub mod_data: Box<[i8]>,

pub sound1: StandardSoundState,
Expand Down

0 comments on commit 0b6b679

Please sign in to comment.