From 0a66d2cb86dff51da3bc5dfe83a02dcf7032a0f2 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Mon, 9 Oct 2023 14:25:17 +1100 Subject: [PATCH] change messages / add emulator pause / refactor threading for gb_emu --- gb-emu/src/debug.rs | 2 +- gb-emu/src/main.rs | 253 +++++++++++++++++++---------------------- gb-emu/src/window.rs | 126 +++++++++++++------- gb-vst/src/lib.rs | 5 +- lib/src/connect/mod.rs | 8 +- lib/src/lib.rs | 56 +++++---- lib/src/util.rs | 10 +- 7 files changed, 248 insertions(+), 212 deletions(-) diff --git a/gb-emu/src/debug.rs b/gb-emu/src/debug.rs index 9e2c0ea..7b09e7a 100644 --- a/gb-emu/src/debug.rs +++ b/gb-emu/src/debug.rs @@ -105,7 +105,7 @@ impl Debugger { return; } } - self.core.run(); + self.core.run(1); } fn should_pause(&mut self) -> bool { diff --git a/gb-emu/src/main.rs b/gb-emu/src/main.rs index 71fa74d..acbc324 100644 --- a/gb-emu/src/main.rs +++ b/gb-emu/src/main.rs @@ -92,146 +92,131 @@ impl Default for StandaloneConfig { fn main() { let args = Args::parse(); - EmulatorHandler::run(args); + run(args); } -enum EmulatorTypes { - Debug(Debugger), - Normal(Box), -} +fn run(args: Args) -> ! { + let (sender, receiver) = channel::(); -struct EmulatorHandler { - emu: EmulatorTypes, - window_manager: WindowManager, - since: Instant, -} - -impl EmulatorHandler { - fn run(args: Args) -> ! { - let (sender, receiver) = channel::(); - - { - let sender = sender.clone(); - ctrlc::set_handler(move || { - sender.send(EmulatorMessage::Stop).unwrap(); - }) - .unwrap(); - } - - let config_manager = ConfigManager::get().expect("Could not open config folder"); - let config = config_manager.load_or_create_base_config(); - let standalone_config: StandaloneConfig = - config_manager.load_or_create_config("standalone"); - - let (output, _stream) = audio::create_output(args.mute); - let rom_file = RomFile::Path(PathBuf::from(args.rom)); - - let (rom, camera) = rom_file - .load( - args.save.map(SramType::File).unwrap_or(SramType::Auto), - NoCamera::default(), - ) - .expect("Error parsing rom"); - - let will_be_cgb = rom.rom_type == CgbRomType::CgbOnly || config.prefer_cgb; - - let shader_path = if will_be_cgb { - config.vulkan_config.cgb_shader_path.as_ref() - } else { - config.vulkan_config.dmg_shader_path.as_ref() - } - .map(|v| config_manager.dir().join(v)); - - let resizable = shader_path.is_some() - && if will_be_cgb { - config.vulkan_config.cgb_shader_resizable - } else { - config.vulkan_config.dmg_shader_resizable - }; - - let scale_override = match if will_be_cgb { - config.vulkan_config.cgb_resolution_override - } else { - config.vulkan_config.dmg_resolution_override - } { - gb_emu_lib::config::ResolutionOverride::Scale(scale) => Some(scale), - gb_emu_lib::config::ResolutionOverride::Default => None, - } - .unwrap_or(standalone_config.scale_factor); - - let mut window_manager = WindowManager::new(sender); - - let window = window_manager.add( - scale_override, - Some(Gilrs::new().unwrap()), - shader_path, - resizable, - ); - - let tile_window: Option = if args.tile_window { - Some(window_manager.add(standalone_config.scale_factor, None, None, false)) - } else { - None - }; - - let layer_window: Option = if args.layer_window { - Some(window_manager.add(standalone_config.scale_factor.min(2), None, None, false)) - } else { - None - }; - - let options = - EmulatorOptions::new_with_config(config, config_manager.dir(), window, rom, output) - .with_serial_target(if args.ascii { - SerialTarget::Stdout(StdoutType::Ascii) - } else if args.hex { - SerialTarget::Stdout(StdoutType::Hex) - } else { - SerialTarget::None - }) - .with_no_save(args.no_save) - .with_tile_window(tile_window) - .with_layer_window(layer_window); - - // let core: Box = if args.camera { - // Box::new(EmulatorCore::init(receiver, options, Webcam::new())) - // } else { - // Box::new(EmulatorCore::init(receiver, options, NoCamera::default())) - // }; - - #[cfg(not(feature = "camera"))] - let core: Box = - Box::new(EmulatorCore::init(receiver, options, camera)); - #[cfg(feature = "camera")] - let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new())); - - let emu = if args.debug { - EmulatorTypes::Debug(Debugger::new(core)) - } else { - EmulatorTypes::Normal(core) - }; - - let since = Instant::now(); - - let mut h = Self { - emu, - window_manager, - since, - }; - loop { - h.cycle(); - } + { + let sender = sender.clone(); + ctrlc::set_handler(move || { + sender.send(EmulatorMessage::Exit).unwrap(); + }) + .unwrap(); } - fn cycle(&mut self) { - 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(), + let config_manager = ConfigManager::get().expect("Could not open config folder"); + let config = config_manager.load_or_create_base_config(); + let standalone_config: StandaloneConfig = config_manager.load_or_create_config("standalone"); + + let (output, stream) = audio::create_output(args.mute); + + let rom_file = RomFile::Path(PathBuf::from(args.rom)); + + let (rom, camera) = rom_file + .load( + args.save.map(SramType::File).unwrap_or(SramType::Auto), + NoCamera::default(), + ) + .expect("Error parsing rom"); + + let will_be_cgb = rom.rom_type == CgbRomType::CgbOnly || config.prefer_cgb; + + let shader_path = if will_be_cgb { + config.vulkan_config.cgb_shader_path.as_ref() + } else { + config.vulkan_config.dmg_shader_path.as_ref() + } + .map(|v| config_manager.dir().join(v)); + + let resizable = shader_path.is_some() + && if will_be_cgb { + config.vulkan_config.cgb_shader_resizable + } else { + config.vulkan_config.dmg_shader_resizable + }; + + let scale_override = match if will_be_cgb { + config.vulkan_config.cgb_resolution_override + } else { + config.vulkan_config.dmg_resolution_override + } { + gb_emu_lib::config::ResolutionOverride::Scale(scale) => Some(scale), + gb_emu_lib::config::ResolutionOverride::Default => None, + } + .unwrap_or(standalone_config.scale_factor); + + let mut window_manager = WindowManager::new(sender, stream); + + let window = window_manager.add( + scale_override, + Some(Gilrs::new().unwrap()), + shader_path, + resizable, + ); + + let tile_window: Option = if args.tile_window { + Some(window_manager.add(standalone_config.scale_factor, None, None, false)) + } else { + None + }; + + let layer_window: Option = if args.layer_window { + Some(window_manager.add(standalone_config.scale_factor.min(2), None, None, false)) + } else { + None + }; + + let options = + EmulatorOptions::new_with_config(config, config_manager.dir(), window, rom, output) + .with_serial_target(if args.ascii { + SerialTarget::Stdout(StdoutType::Ascii) + } else if args.hex { + SerialTarget::Stdout(StdoutType::Hex) + } else { + SerialTarget::None + }) + .with_no_save(args.no_save) + .with_tile_window(tile_window) + .with_layer_window(layer_window); + + // let core: Box = if args.camera { + // Box::new(EmulatorCore::init(receiver, options, Webcam::new())) + // } else { + // Box::new(EmulatorCore::init(receiver, options, NoCamera::default())) + // }; + + // #[cfg(not(feature = "camera"))] + // let core: Box = + // Box::new(EmulatorCore::init(receiver, options, camera)); + // #[cfg(feature = "camera")] + // let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new())); + + // let emu = if args.debug { + // EmulatorTypes::Debug(Debugger::new(core)) + // } else { + // EmulatorTypes::Normal(core) + // }; + + let mut core = EmulatorCore::init(true, receiver, options, camera); + + if args.debug { + let mut debugger = Debugger::new(Box::new(core)); + let mut since = Instant::now(); + loop { + if since.elapsed() >= UPDATE_INTERVAL { + window_manager.update_events(); + since = Instant::now(); + } + debugger.step(); } + } else { + std::thread::spawn(move || loop { + core.run(10000); + }); + + window_manager.run_events_blocking(); } } diff --git a/gb-emu/src/window.rs b/gb-emu/src/window.rs index 31341ca..a127e06 100644 --- a/gb-emu/src/window.rs +++ b/gb-emu/src/window.rs @@ -4,6 +4,7 @@ use std::{ sync::{mpsc::Sender, Arc, Mutex, RwLock}, }; +use cpal::Stream; use gb_emu_lib::{ connect::{EmulatorMessage, JoypadState, Renderer, ResolutionData}, renderer::{RendererBackend, RendererBackendManager, WindowOptions}, @@ -17,7 +18,7 @@ use raw_window_handle::HasRawDisplayHandle; use winit::{ dpi::PhysicalSize, event::{Event, VirtualKeyCode, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, platform::run_return::EventLoopExtRunReturn, window::{Window, WindowBuilder, WindowId}, }; @@ -36,14 +37,19 @@ struct WindowData { pub struct WindowManager { event_loop: EventLoop<()>, + data: WindowManagerData, +} + +struct WindowManagerData { windows: HashMap>, window_data_manager: Arc, input: Arc>, sender: Sender, + _stream: Stream, } impl WindowManager { - pub(crate) fn new(sender: Sender) -> Self { + pub(crate) fn new(sender: Sender, _stream: Stream) -> Self { let event_loop = EventLoop::new(); #[cfg(feature = "vulkan")] let window_data_manager = @@ -52,10 +58,13 @@ impl WindowManager { let window_data_manager = Arc::new(RendererBackendManager::new()); Self { event_loop, - windows: HashMap::new(), - window_data_manager, - input: Arc::new(Mutex::new(WinitInputHelper::new())), - sender, + data: WindowManagerData { + windows: HashMap::new(), + window_data_manager, + input: Arc::new(Mutex::new(WinitInputHelper::new())), + sender, + _stream, + }, } } @@ -65,12 +74,43 @@ impl WindowManager { gamepad_handler: Option, shader_path: Option, 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, + shader_path: Option, + resizable: bool, + event_loop: &EventLoop<()>, ) -> WindowRenderer { let (r, info) = WindowRenderer::new( factor, gamepad_handler, self.input.clone(), - &self.event_loop, + event_loop, self.window_data_manager.clone(), shader_path, resizable, @@ -79,44 +119,54 @@ impl WindowManager { r } - pub fn update_events(&mut self) { - self.event_loop.run_return(|event, _, control_flow| { - control_flow.set_wait(); - if let Ok(mut i) = self.input.lock() { - i.update(&event); - } + fn handler( + &self, + run_return: bool, + event: Event<'_, ()>, + _target: &EventLoopWindowTarget<()>, + control_flow: &mut ControlFlow, + ) { + control_flow.set_wait(); - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id: _, - } => { - self.sender.send(EmulatorMessage::Stop).unwrap(); - } - Event::MainEventsCleared => { + if let Ok(mut i) = self.input.lock() { + i.update(&event); + } + + match event { + Event::Resumed => { + self.sender.send(EmulatorMessage::Start).unwrap(); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + window_id: _, + } => { + self.sender.send(EmulatorMessage::Exit).unwrap(); + } + Event::MainEventsCleared => { + if run_return { control_flow.set_exit(); } - Event::RedrawRequested(window_id) => { - if let Some(w) = self.windows.get(&window_id) { - if let Ok(mut renderer) = w.renderer.lock() { - let inner_size = w.window.inner_size(); - if let Ok(rendered_size) = w.rendered_size.read() { - renderer.render( - ResolutionData { - real_width: inner_size.width, - real_height: inner_size.height, - scaled_width: rendered_size.0, - scaled_height: rendered_size.1, - }, - &self.window_data_manager, - ); - } + } + Event::RedrawRequested(window_id) => { + if let Some(w) = self.windows.get(&window_id) { + if let Ok(mut renderer) = w.renderer.lock() { + let inner_size = w.window.inner_size(); + if let Ok(rendered_size) = w.rendered_size.read() { + renderer.render( + ResolutionData { + real_width: inner_size.width, + real_height: inner_size.height, + scaled_width: rendered_size.0, + scaled_height: rendered_size.1, + }, + &self.window_data_manager, + ); } } } - _ => {} } - }); + _ => {} + } } } diff --git a/gb-vst/src/lib.rs b/gb-vst/src/lib.rs index 8d91398..5ee8b97 100644 --- a/gb-vst/src/lib.rs +++ b/gb-vst/src/lib.rs @@ -329,7 +329,7 @@ impl Plugin for GameboyEmu { .with_sram_buffer(self.params.sram_save.state.clone()) .with_show_bootrom(!will_skip_bootrom); - EmulatorCore::init(receiver, options, camera) + EmulatorCore::init(false, receiver, options, camera) }; emulator_core.run_until_buffer_full(); @@ -346,9 +346,8 @@ impl Plugin for GameboyEmu { } fn deactivate(&mut self) { - eprintln!("DEACTIVATE FUNCTION"); if let Some(ref mut vars) = self.vars { - match vars.sender.send(EmulatorMessage::Stop) { + match vars.sender.send(EmulatorMessage::Exit) { Ok(_) => self.vars = None, Err(e) => nih_log!("error {e} sending message to emulator"), } diff --git a/lib/src/connect/mod.rs b/lib/src/connect/mod.rs index 978103e..8d747d0 100644 --- a/lib/src/connect/mod.rs +++ b/lib/src/connect/mod.rs @@ -12,8 +12,11 @@ use crate::processor::memory::Rom; pub use crate::{HEIGHT, WIDTH}; use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb}; +#[derive(Debug)] pub enum EmulatorMessage { - Stop, + Start, + Pause, + Exit, } #[derive(Clone, Copy)] @@ -356,8 +359,7 @@ pub trait EmulatorCoreTrait { fn pc(&self) -> u16; fn print_reg(&self) -> String; fn get_memory(&self, address: u16) -> u8; - fn run(&mut self); - fn run_stepped(&mut self, step_size: usize); + fn run(&mut self, cycles: usize); fn run_until_buffer_full(&mut self); fn process_messages(&mut self); } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 703dc57..73dab24 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,9 +1,6 @@ #![feature(exclusive_range_pattern, let_chains, bigint_helper_methods)] -use crate::{ - processor::{memory::Memory, Flags}, - util::pause, -}; +use crate::processor::{memory::Memory, Flags}; use connect::{ AudioOutput, CameraWrapper, EmulatorCoreTrait, EmulatorMessage, EmulatorOptions, PocketCamera, Renderer, RomFile, @@ -13,7 +10,6 @@ use processor::{ Cpu, }; use std::{ - io::{stdout, Write}, marker::PhantomData, process::exit, sync::{mpsc::Receiver, Arc, Mutex}, @@ -46,6 +42,7 @@ where { receiver: Receiver, cpu: Cpu, + paused: bool, spooky: PhantomData, } @@ -56,6 +53,7 @@ where C: PocketCamera + Send + 'static, { pub fn init( + paused: bool, receiver: Receiver, mut options: EmulatorOptions, camera: Arc>>, @@ -84,6 +82,7 @@ where )); Self::new( + paused, receiver, Cpu::new( Memory::init( @@ -104,10 +103,15 @@ where ) } - fn new(receiver: Receiver, cpu: Cpu) -> Self { + fn new( + paused: bool, + receiver: Receiver, + cpu: Cpu, + ) -> Self { Self { receiver, cpu, + paused, spooky: PhantomData, } } @@ -144,16 +148,26 @@ where fn process_messages(&mut self) { while let Ok(msg) = self.receiver.try_recv() { - #[allow(clippy::single_match, unreachable_patterns)] - match msg { - EmulatorMessage::Stop => { - self.cpu.memory.flush_rom(); - exit(0); - } - _ => {} + 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 { + EmulatorMessage::Exit => { + self.cpu.memory.flush_rom(); + exit(0); + } + EmulatorMessage::Start => self.paused = false, + EmulatorMessage::Pause => self.paused = true, + } + } } impl EmulatorCoreTrait for EmulatorCore @@ -193,25 +207,19 @@ where self.cpu.memory.get(address) } - fn run(&mut self) { + fn run(&mut self, cycles: usize) { self.process_messages(); - self.run_cycle(); - } - - fn run_stepped(&mut self, step_size: usize) { - loop { - self.process_messages(); - for _ in 0..step_size { + if !self.paused { + for _ in 0..cycles { self.run_cycle(); } - stdout().flush().unwrap(); - pause(); } } fn run_until_buffer_full(&mut self) { + self.process_messages(); while !self.cpu.memory.is_audio_buffer_full() { - self.run(); + self.run_cycle(); } } diff --git a/lib/src/util.rs b/lib/src/util.rs index 8691a8b..2c799a7 100644 --- a/lib/src/util.rs +++ b/lib/src/util.rs @@ -1,15 +1,7 @@ use num_traits::{PrimInt, Unsigned}; use crate::processor::{memory::mmio::gpu::Colour, Direction}; -use std::{io, mem::transmute}; - -pub(crate) fn pause() -> String { - let mut line = String::new(); - match io::stdin().read_line(&mut line) { - Ok(_) => line, - Err(_) => String::from(""), - } -} +use std::mem::transmute; pub(crate) fn as_signed(unsigned: u8) -> i8 { unsafe { transmute(unsigned) }