change messages / add emulator pause / refactor threading for gb_emu

This commit is contained in:
Alex Janka 2023-10-09 14:25:17 +11:00
parent 7cfe34fe82
commit 0a66d2cb86
7 changed files with 248 additions and 212 deletions

View file

@ -105,7 +105,7 @@ impl Debugger {
return; return;
} }
} }
self.core.run(); self.core.run(1);
} }
fn should_pause(&mut self) -> bool { fn should_pause(&mut self) -> bool {

View file

@ -92,38 +92,26 @@ impl Default for StandaloneConfig {
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
EmulatorHandler::run(args); run(args);
} }
enum EmulatorTypes {
Debug(Debugger),
Normal(Box<dyn EmulatorCoreTrait>),
}
struct EmulatorHandler {
emu: EmulatorTypes,
window_manager: WindowManager,
since: Instant,
}
impl EmulatorHandler {
fn run(args: Args) -> ! { fn run(args: Args) -> ! {
let (sender, receiver) = channel::<EmulatorMessage>(); let (sender, receiver) = channel::<EmulatorMessage>();
{ {
let sender = sender.clone(); let sender = sender.clone();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
sender.send(EmulatorMessage::Stop).unwrap(); sender.send(EmulatorMessage::Exit).unwrap();
}) })
.unwrap(); .unwrap();
} }
let config_manager = ConfigManager::get().expect("Could not open config folder"); let config_manager = ConfigManager::get().expect("Could not open config folder");
let config = config_manager.load_or_create_base_config(); let config = config_manager.load_or_create_base_config();
let standalone_config: StandaloneConfig = let standalone_config: StandaloneConfig = config_manager.load_or_create_config("standalone");
config_manager.load_or_create_config("standalone");
let (output, stream) = audio::create_output(args.mute);
let (output, _stream) = audio::create_output(args.mute);
let rom_file = RomFile::Path(PathBuf::from(args.rom)); let rom_file = RomFile::Path(PathBuf::from(args.rom));
let (rom, camera) = rom_file let (rom, camera) = rom_file
@ -159,7 +147,7 @@ impl EmulatorHandler {
} }
.unwrap_or(standalone_config.scale_factor); .unwrap_or(standalone_config.scale_factor);
let mut window_manager = WindowManager::new(sender); let mut window_manager = WindowManager::new(sender, stream);
let window = window_manager.add( let window = window_manager.add(
scale_override, scale_override,
@ -199,39 +187,36 @@ impl EmulatorHandler {
// Box::new(EmulatorCore::init(receiver, options, NoCamera::default())) // Box::new(EmulatorCore::init(receiver, options, NoCamera::default()))
// }; // };
#[cfg(not(feature = "camera"))] // #[cfg(not(feature = "camera"))]
let core: Box<dyn EmulatorCoreTrait> = // let core: Box<dyn EmulatorCoreTrait> =
Box::new(EmulatorCore::init(receiver, options, camera)); // Box::new(EmulatorCore::init(receiver, options, camera));
#[cfg(feature = "camera")] // #[cfg(feature = "camera")]
let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new())); // let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new()));
let emu = if args.debug { // let emu = if args.debug {
EmulatorTypes::Debug(Debugger::new(core)) // EmulatorTypes::Debug(Debugger::new(core))
} else { // } else {
EmulatorTypes::Normal(core) // EmulatorTypes::Normal(core)
}; // };
let since = Instant::now(); let mut core = EmulatorCore::init(true, receiver, options, camera);
let mut h = Self { if args.debug {
emu, let mut debugger = Debugger::new(Box::new(core));
window_manager, let mut since = Instant::now();
since,
};
loop { loop {
h.cycle(); if since.elapsed() >= UPDATE_INTERVAL {
window_manager.update_events();
since = Instant::now();
} }
debugger.step();
} }
} else {
std::thread::spawn(move || loop {
core.run(10000);
});
fn cycle(&mut self) { window_manager.run_events_blocking();
if self.since.elapsed() >= UPDATE_INTERVAL {
self.window_manager.update_events();
self.since = Instant::now();
}
match self.emu {
EmulatorTypes::Debug(ref mut debugger) => debugger.step(),
EmulatorTypes::Normal(ref mut core) => core.run(),
}
} }
} }

View file

@ -4,6 +4,7 @@ use std::{
sync::{mpsc::Sender, Arc, Mutex, RwLock}, sync::{mpsc::Sender, Arc, Mutex, RwLock},
}; };
use cpal::Stream;
use gb_emu_lib::{ use gb_emu_lib::{
connect::{EmulatorMessage, JoypadState, Renderer, ResolutionData}, connect::{EmulatorMessage, JoypadState, Renderer, ResolutionData},
renderer::{RendererBackend, RendererBackendManager, WindowOptions}, renderer::{RendererBackend, RendererBackendManager, WindowOptions},
@ -17,7 +18,7 @@ use raw_window_handle::HasRawDisplayHandle;
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{Event, VirtualKeyCode, WindowEvent}, event::{Event, VirtualKeyCode, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
platform::run_return::EventLoopExtRunReturn, platform::run_return::EventLoopExtRunReturn,
window::{Window, WindowBuilder, WindowId}, window::{Window, WindowBuilder, WindowId},
}; };
@ -36,14 +37,19 @@ struct WindowData {
pub struct WindowManager { pub struct WindowManager {
event_loop: EventLoop<()>, event_loop: EventLoop<()>,
data: WindowManagerData,
}
struct WindowManagerData {
windows: HashMap<WindowId, Arc<WindowData>>, windows: HashMap<WindowId, Arc<WindowData>>,
window_data_manager: Arc<RendererBackendManager>, window_data_manager: Arc<RendererBackendManager>,
input: Arc<Mutex<WinitInputHelper>>, input: Arc<Mutex<WinitInputHelper>>,
sender: Sender<EmulatorMessage>, sender: Sender<EmulatorMessage>,
_stream: Stream,
} }
impl WindowManager { impl WindowManager {
pub(crate) fn new(sender: Sender<EmulatorMessage>) -> Self { pub(crate) fn new(sender: Sender<EmulatorMessage>, _stream: Stream) -> Self {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
let window_data_manager = let window_data_manager =
@ -52,10 +58,13 @@ impl WindowManager {
let window_data_manager = Arc::new(RendererBackendManager::new()); let window_data_manager = Arc::new(RendererBackendManager::new());
Self { Self {
event_loop, event_loop,
data: WindowManagerData {
windows: HashMap::new(), windows: HashMap::new(),
window_data_manager, window_data_manager,
input: Arc::new(Mutex::new(WinitInputHelper::new())), input: Arc::new(Mutex::new(WinitInputHelper::new())),
sender, sender,
_stream,
},
} }
} }
@ -65,12 +74,43 @@ impl WindowManager {
gamepad_handler: Option<Gilrs>, gamepad_handler: Option<Gilrs>,
shader_path: Option<PathBuf>, shader_path: Option<PathBuf>,
resizable: bool, resizable: bool,
) -> WindowRenderer {
self.data.add(
factor,
gamepad_handler,
shader_path,
resizable,
&self.event_loop,
)
}
pub fn update_events(&mut self) {
self.event_loop.run_return(|event, target, control_flow| {
self.data.handler(true, event, target, control_flow)
});
}
pub fn run_events_blocking(self) -> ! {
self.event_loop.run(move |event, target, control_flow| {
self.data.handler(false, event, target, control_flow)
})
}
}
impl WindowManagerData {
fn add(
&mut self,
factor: usize,
gamepad_handler: Option<Gilrs>,
shader_path: Option<PathBuf>,
resizable: bool,
event_loop: &EventLoop<()>,
) -> WindowRenderer { ) -> WindowRenderer {
let (r, info) = WindowRenderer::new( let (r, info) = WindowRenderer::new(
factor, factor,
gamepad_handler, gamepad_handler,
self.input.clone(), self.input.clone(),
&self.event_loop, event_loop,
self.window_data_manager.clone(), self.window_data_manager.clone(),
shader_path, shader_path,
resizable, resizable,
@ -79,23 +119,34 @@ impl WindowManager {
r r
} }
pub fn update_events(&mut self) { fn handler(
self.event_loop.run_return(|event, _, control_flow| { &self,
run_return: bool,
event: Event<'_, ()>,
_target: &EventLoopWindowTarget<()>,
control_flow: &mut ControlFlow,
) {
control_flow.set_wait(); control_flow.set_wait();
if let Ok(mut i) = self.input.lock() { if let Ok(mut i) = self.input.lock() {
i.update(&event); i.update(&event);
} }
match event { match event {
Event::Resumed => {
self.sender.send(EmulatorMessage::Start).unwrap();
}
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id: _, window_id: _,
} => { } => {
self.sender.send(EmulatorMessage::Stop).unwrap(); self.sender.send(EmulatorMessage::Exit).unwrap();
} }
Event::MainEventsCleared => { Event::MainEventsCleared => {
if run_return {
control_flow.set_exit(); control_flow.set_exit();
} }
}
Event::RedrawRequested(window_id) => { Event::RedrawRequested(window_id) => {
if let Some(w) = self.windows.get(&window_id) { if let Some(w) = self.windows.get(&window_id) {
if let Ok(mut renderer) = w.renderer.lock() { if let Ok(mut renderer) = w.renderer.lock() {
@ -116,7 +167,6 @@ impl WindowManager {
} }
_ => {} _ => {}
} }
});
} }
} }

View file

@ -329,7 +329,7 @@ impl Plugin for GameboyEmu {
.with_sram_buffer(self.params.sram_save.state.clone()) .with_sram_buffer(self.params.sram_save.state.clone())
.with_show_bootrom(!will_skip_bootrom); .with_show_bootrom(!will_skip_bootrom);
EmulatorCore::init(receiver, options, camera) EmulatorCore::init(false, receiver, options, camera)
}; };
emulator_core.run_until_buffer_full(); emulator_core.run_until_buffer_full();
@ -346,9 +346,8 @@ impl Plugin for GameboyEmu {
} }
fn deactivate(&mut self) { fn deactivate(&mut self) {
eprintln!("DEACTIVATE FUNCTION");
if let Some(ref mut vars) = self.vars { if let Some(ref mut vars) = self.vars {
match vars.sender.send(EmulatorMessage::Stop) { match vars.sender.send(EmulatorMessage::Exit) {
Ok(_) => self.vars = None, Ok(_) => self.vars = None,
Err(e) => nih_log!("error {e} sending message to emulator"), Err(e) => nih_log!("error {e} sending message to emulator"),
} }

View file

@ -12,8 +12,11 @@ use crate::processor::memory::Rom;
pub use crate::{HEIGHT, WIDTH}; pub use crate::{HEIGHT, WIDTH};
use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb}; use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb};
#[derive(Debug)]
pub enum EmulatorMessage { pub enum EmulatorMessage {
Stop, Start,
Pause,
Exit,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -356,8 +359,7 @@ pub trait EmulatorCoreTrait {
fn pc(&self) -> u16; fn pc(&self) -> u16;
fn print_reg(&self) -> String; fn print_reg(&self) -> String;
fn get_memory(&self, address: u16) -> u8; fn get_memory(&self, address: u16) -> u8;
fn run(&mut self); fn run(&mut self, cycles: usize);
fn run_stepped(&mut self, step_size: usize);
fn run_until_buffer_full(&mut self); fn run_until_buffer_full(&mut self);
fn process_messages(&mut self); fn process_messages(&mut self);
} }

View file

@ -1,9 +1,6 @@
#![feature(exclusive_range_pattern, let_chains, bigint_helper_methods)] #![feature(exclusive_range_pattern, let_chains, bigint_helper_methods)]
use crate::{ use crate::processor::{memory::Memory, Flags};
processor::{memory::Memory, Flags},
util::pause,
};
use connect::{ use connect::{
AudioOutput, CameraWrapper, EmulatorCoreTrait, EmulatorMessage, EmulatorOptions, PocketCamera, AudioOutput, CameraWrapper, EmulatorCoreTrait, EmulatorMessage, EmulatorOptions, PocketCamera,
Renderer, RomFile, Renderer, RomFile,
@ -13,7 +10,6 @@ use processor::{
Cpu, Cpu,
}; };
use std::{ use std::{
io::{stdout, Write},
marker::PhantomData, marker::PhantomData,
process::exit, process::exit,
sync::{mpsc::Receiver, Arc, Mutex}, sync::{mpsc::Receiver, Arc, Mutex},
@ -46,6 +42,7 @@ where
{ {
receiver: Receiver<EmulatorMessage>, receiver: Receiver<EmulatorMessage>,
cpu: Cpu<ColourFormat, R, C>, cpu: Cpu<ColourFormat, R, C>,
paused: bool,
spooky: PhantomData<C>, spooky: PhantomData<C>,
} }
@ -56,6 +53,7 @@ where
C: PocketCamera + Send + 'static, C: PocketCamera + Send + 'static,
{ {
pub fn init( pub fn init(
paused: bool,
receiver: Receiver<EmulatorMessage>, receiver: Receiver<EmulatorMessage>,
mut options: EmulatorOptions<ColourFormat, R, C>, mut options: EmulatorOptions<ColourFormat, R, C>,
camera: Arc<Mutex<CameraWrapper<C>>>, camera: Arc<Mutex<CameraWrapper<C>>>,
@ -84,6 +82,7 @@ where
)); ));
Self::new( Self::new(
paused,
receiver, receiver,
Cpu::new( Cpu::new(
Memory::init( Memory::init(
@ -104,10 +103,15 @@ where
) )
} }
fn new(receiver: Receiver<EmulatorMessage>, cpu: Cpu<ColourFormat, R, C>) -> Self { fn new(
paused: bool,
receiver: Receiver<EmulatorMessage>,
cpu: Cpu<ColourFormat, R, C>,
) -> Self {
Self { Self {
receiver, receiver,
cpu, cpu,
paused,
spooky: PhantomData, spooky: PhantomData,
} }
} }
@ -144,14 +148,24 @@ where
fn process_messages(&mut self) { fn process_messages(&mut self) {
while let Ok(msg) = self.receiver.try_recv() { while let Ok(msg) = self.receiver.try_recv() {
#[allow(clippy::single_match, unreachable_patterns)] self.process_message(msg);
}
while self.paused {
match self.receiver.recv() {
Ok(msg) => self.process_message(msg),
Err(e) => panic!("no message sender! error {e:#?}"),
}
}
}
fn process_message(&mut self, msg: EmulatorMessage) {
match msg { match msg {
EmulatorMessage::Stop => { EmulatorMessage::Exit => {
self.cpu.memory.flush_rom(); self.cpu.memory.flush_rom();
exit(0); exit(0);
} }
_ => {} EmulatorMessage::Start => self.paused = false,
} EmulatorMessage::Pause => self.paused = true,
} }
} }
} }
@ -193,25 +207,19 @@ where
self.cpu.memory.get(address) self.cpu.memory.get(address)
} }
fn run(&mut self) { fn run(&mut self, cycles: usize) {
self.process_messages(); self.process_messages();
if !self.paused {
for _ in 0..cycles {
self.run_cycle(); self.run_cycle();
} }
fn run_stepped(&mut self, step_size: usize) {
loop {
self.process_messages();
for _ in 0..step_size {
self.run_cycle();
}
stdout().flush().unwrap();
pause();
} }
} }
fn run_until_buffer_full(&mut self) { fn run_until_buffer_full(&mut self) {
self.process_messages();
while !self.cpu.memory.is_audio_buffer_full() { while !self.cpu.memory.is_audio_buffer_full() {
self.run(); self.run_cycle();
} }
} }

View file

@ -1,15 +1,7 @@
use num_traits::{PrimInt, Unsigned}; use num_traits::{PrimInt, Unsigned};
use crate::processor::{memory::mmio::gpu::Colour, Direction}; use crate::processor::{memory::mmio::gpu::Colour, Direction};
use std::{io, mem::transmute}; use std::mem::transmute;
pub(crate) fn pause() -> String {
let mut line = String::new();
match io::stdin().read_line(&mut line) {
Ok(_) => line,
Err(_) => String::from(""),
}
}
pub(crate) fn as_signed(unsigned: u8) -> i8 { pub(crate) fn as_signed(unsigned: u8) -> i8 {
unsafe { transmute(unsigned) } unsafe { transmute(unsigned) }