working sdl2 example, albeit messy

This commit is contained in:
lif 2019-11-15 20:59:08 -08:00
parent d3f83c48c1
commit e183627018
4 changed files with 38 additions and 46 deletions

View file

@ -6,8 +6,12 @@ use rustro::retro::ffi::GameGeometry;
use rustro::retro::ffi::SystemAvInfo; use rustro::retro::ffi::SystemAvInfo;
use std::ffi::CStr; use std::ffi::CStr;
use std::io::Write;
use std::ops::Deref;
use std::path::{PathBuf, Path}; use std::path::{PathBuf, Path};
use std::time::Duration; use std::pin::Pin;
use std::thread::sleep;
use std::time::{Duration, Instant};
use structopt::StructOpt; use structopt::StructOpt;
@ -16,7 +20,6 @@ use sdl2::rect::Rect;
use sdl2::event::Event; use sdl2::event::Event;
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::WindowContext; use sdl2::video::WindowContext;
use std::io::Write;
struct MyEmulator { struct MyEmulator {
retro: retro::wrapper::LibretroWrapper, retro: retro::wrapper::LibretroWrapper,
@ -26,7 +29,7 @@ struct MyEmulator {
} }
impl MyEmulator { impl MyEmulator {
pub fn with_core(core: impl AsRef<Path>) -> Self { pub fn with_core(core: impl AsRef<Path>) -> Pin<Box<Self>> {
let lib = libloading::Library::new(core.as_ref()).unwrap(); let lib = libloading::Library::new(core.as_ref()).unwrap();
let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap(); let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap();
let retro = retro::wrapper::LibretroWrapper::from(raw_retro); let retro = retro::wrapper::LibretroWrapper::from(raw_retro);
@ -36,11 +39,12 @@ impl MyEmulator {
let av_info = retro.as_ref().get_system_av_info(); let av_info = retro.as_ref().get_system_av_info();
let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555; let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555;
let mut result = MyEmulator { retro, av_info, pixel_format, texture: None }; let mut emu = MyEmulator { retro, av_info, pixel_format, texture: None };
let mut pin_emu = Box::pin(emu);
retro::wrapper::register_handler(&mut result); retro::wrapper::register_handler(pin_emu.as_mut());
result.retro.as_ref().init(); pin_emu.retro.as_ref().init();
result pin_emu
} }
pub fn run(&self) { pub fn run(&self) {
@ -58,20 +62,9 @@ impl MyEmulator {
impl retro::convert::Handler for MyEmulator { impl retro::convert::Handler for MyEmulator {
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) { fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
println!("self ref: {:?}", self as *mut Self); let rect = Rect::new(0, 0, width, height);
//println!("video_refresh {}x{}", width, height);
//let rect = Rect::new(0, 0, width, height);
if let Some(tex) = self.texture.as_mut() { if let Some(tex) = self.texture.as_mut() {
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| { tex.update(rect, data, pitch as usize).unwrap();
let src_pitch = pitch as usize;
let src_width = width as usize;
for y in 0..(height as usize) {
let src_slice = (y * src_pitch)..((y * src_pitch) + src_width);
let tex_slice = (y * tex_pitch)..((y * tex_pitch) + src_width);
tex_buffer[tex_slice].copy_from_slice(&data[src_slice]);
}
}).unwrap();
//tex.update(None, data, pitch as usize).unwrap();
} }
} }
@ -85,6 +78,14 @@ impl retro::convert::Handler for MyEmulator {
} }
} }
impl Drop for MyEmulator {
fn drop(&mut self) {
if let Some(x) = self.texture.take() {
unsafe { x.destroy(); }
}
}
}
pub fn main() -> failure::Fallible<()> { pub fn main() -> failure::Fallible<()> {
let opt: Opt = Opt::from_args(); let opt: Opt = Opt::from_args();
let mut emu = MyEmulator::with_core(&opt.core); let mut emu = MyEmulator::with_core(&opt.core);
@ -102,11 +103,12 @@ pub fn main() -> failure::Fallible<()> {
let texture_creator = canvas.texture_creator(); let texture_creator = canvas.texture_creator();
emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok(); emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok();
let mut x: u8 = 1; let target_frame_time = Duration::from_secs_f64(1.0 / emu.av_info.timing.fps);
println!("emu ref: {:?}", &emu as *const MyEmulator);
let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?; let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?;
'running: loop { 'running: loop {
let frame_begin = Instant::now();
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
Event::Quit {..} Event::Quit {..}
@ -116,22 +118,14 @@ pub fn main() -> failure::Fallible<()> {
_ => {} _ => {}
} }
} }
if let Some(tex) = emu.texture.as_mut() {
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
for i in 0..tex_buffer.len() {
tex_buffer[i] = x;
x = (x + 1) % 255;
}
}).unwrap();
//tex.update(None, data, pitch as usize).unwrap();
}
// The rest of the game loop goes here... // The rest of the game loop goes here...
emu.run(); emu.run();
canvas.clear(); canvas.clear();
canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?; canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?;
canvas.present(); canvas.present();
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
target_frame_time.checked_sub(frame_begin.elapsed()).map(sleep);
} }
Ok(()) Ok(())
} }

View file

@ -189,7 +189,7 @@ pub enum EnvCmd {
// SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS, // SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS,
} }
pub trait Handler: 'static { pub trait Handler: Unpin + 'static {
fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {} fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {}
fn audio_sample(&mut self, left: i16, right: i16) {} fn audio_sample(&mut self, left: i16, right: i16) {}
fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() } fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() }
@ -235,3 +235,5 @@ pub trait Handler: 'static {
fn get_language(&mut self) -> Option<Language> { None } fn get_language(&mut self) -> Option<Language> { None }
// fn set_serialization_quirks(&mut self, quirks: &mut u64) -> bool { false } // fn set_serialization_quirks(&mut self, quirks: &mut u64) -> bool { false }
} }
// impl<T: Handler> Unpin for T {}

View file

@ -1,13 +1,10 @@
use std::convert::TryInto;
use std::os::raw::{c_char, c_void}; use std::os::raw::{c_char, c_void};
use std::path::Path; use std::path::Path;
use std::marker::PhantomData;
use failure::Fallible; use failure::Fallible;
use libloading; use libloading;
use super::ffi::*; use super::ffi::*;
use super::convert::*;
pub struct LibretroApi { pub struct LibretroApi {
lib: libloading::Library, // for tying our lifetime to its own lib: libloading::Library, // for tying our lifetime to its own

View file

@ -4,10 +4,12 @@ use core::slice::from_raw_parts;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::{c_uint, c_char}; use std::os::raw::{c_uint, c_char};
use std::ops::DerefMut;
use std::path::Path; use std::path::Path;
use std::pin::Pin;
use libretro_sys::{Message, PixelFormat, SystemAvInfo, GameGeometry}; use libretro_sys::{Message, PixelFormat, SystemAvInfo, GameGeometry};
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::TryFromPrimitive;
use super::convert::*; use super::convert::*;
use super::loading::*; use super::loading::*;
@ -18,7 +20,7 @@ static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
#[derive(Default)] #[derive(Default)]
struct StaticCallbacks { struct StaticCallbacks {
handler: Option<&'static mut dyn Handler>, handler: Option<Pin<&'static mut dyn Handler>>,
} }
unsafe impl Sync for StaticCallbacks {} unsafe impl Sync for StaticCallbacks {}
@ -135,11 +137,6 @@ impl StaticCallbacks {
None => 0, None => 0,
} }
} }
pub(crate) fn register(cb: &'static mut dyn Handler) {
unsafe {
CB_SINGLETON.handler.replace(cb);
}
}
pub(crate) fn reset() { pub(crate) fn reset() {
unsafe { unsafe {
CB_SINGLETON.handler.take(); CB_SINGLETON.handler.take();
@ -177,7 +174,9 @@ impl Drop for LibretroWrapper {
// a note on lifetimes: we explicitly lie about them here because as long as they live as long as // a note on lifetimes: we explicitly lie about them here because as long as they live as long as
// the library wrapper itself we're good (we wipe our 'static references on drop() too) // the library wrapper itself we're good (we wipe our 'static references on drop() too)
pub fn register_handler(handler: &mut dyn Handler) { pub fn register_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
let ptr: *mut dyn Handler = handler; unsafe {
StaticCallbacks::register(unsafe { ptr.as_mut() }.unwrap()); let mut ptr = handler.get_unchecked_mut() as *mut dyn Handler;
CB_SINGLETON.handler.replace(Pin::new_unchecked(ptr.as_mut().unwrap()));
}
} }