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;
}
}
self.core.run();
self.core.run(1);
}
fn should_pause(&mut self) -> bool {

View file

@ -92,38 +92,26 @@ impl Default for StandaloneConfig {
fn main() {
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) -> ! {
let (sender, receiver) = channel::<EmulatorMessage>();
{
let sender = sender.clone();
ctrlc::set_handler(move || {
sender.send(EmulatorMessage::Stop).unwrap();
sender.send(EmulatorMessage::Exit).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 standalone_config: StandaloneConfig = 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, camera) = rom_file
@ -159,7 +147,7 @@ impl EmulatorHandler {
}
.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(
scale_override,
@ -199,39 +187,36 @@ impl EmulatorHandler {
// Box::new(EmulatorCore::init(receiver, options, NoCamera::default()))
// };
#[cfg(not(feature = "camera"))]
let core: Box<dyn EmulatorCoreTrait> =
Box::new(EmulatorCore::init(receiver, options, camera));
#[cfg(feature = "camera")]
let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new()));
// #[cfg(not(feature = "camera"))]
// let core: Box<dyn EmulatorCoreTrait> =
// 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 emu = if args.debug {
// EmulatorTypes::Debug(Debugger::new(core))
// } else {
// EmulatorTypes::Normal(core)
// };
let since = Instant::now();
let mut core = EmulatorCore::init(true, receiver, options, camera);
let mut h = Self {
emu,
window_manager,
since,
};
if args.debug {
let mut debugger = Debugger::new(Box::new(core));
let mut since = Instant::now();
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) {
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(),
}
window_manager.run_events_blocking();
}
}

View file

@ -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<WindowId, Arc<WindowData>>,
window_data_manager: Arc<RendererBackendManager>,
input: Arc<Mutex<WinitInputHelper>>,
sender: Sender<EmulatorMessage>,
_stream: Stream,
}
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();
#[cfg(feature = "vulkan")]
let window_data_manager =
@ -52,10 +58,13 @@ impl WindowManager {
let window_data_manager = Arc::new(RendererBackendManager::new());
Self {
event_loop,
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<Gilrs>,
shader_path: Option<PathBuf>,
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 {
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,23 +119,34 @@ impl WindowManager {
r
}
pub fn update_events(&mut self) {
self.event_loop.run_return(|event, _, control_flow| {
fn handler(
&self,
run_return: bool,
event: Event<'_, ()>,
_target: &EventLoopWindowTarget<()>,
control_flow: &mut ControlFlow,
) {
control_flow.set_wait();
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::Stop).unwrap();
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() {
@ -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_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"),
}

View file

@ -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);
}

View file

@ -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<EmulatorMessage>,
cpu: Cpu<ColourFormat, R, C>,
paused: bool,
spooky: PhantomData<C>,
}
@ -56,6 +53,7 @@ where
C: PocketCamera + Send + 'static,
{
pub fn init(
paused: bool,
receiver: Receiver<EmulatorMessage>,
mut options: EmulatorOptions<ColourFormat, R, C>,
camera: Arc<Mutex<CameraWrapper<C>>>,
@ -84,6 +82,7 @@ where
));
Self::new(
paused,
receiver,
Cpu::new(
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 {
receiver,
cpu,
paused,
spooky: PhantomData,
}
}
@ -144,14 +148,24 @@ where
fn process_messages(&mut self) {
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 {
EmulatorMessage::Stop => {
EmulatorMessage::Exit => {
self.cpu.memory.flush_rom();
exit(0);
}
_ => {}
}
EmulatorMessage::Start => self.paused = false,
EmulatorMessage::Pause => self.paused = true,
}
}
}
@ -193,25 +207,19 @@ where
self.cpu.memory.get(address)
}
fn run(&mut self) {
fn run(&mut self, cycles: usize) {
self.process_messages();
if !self.paused {
for _ in 0..cycles {
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) {
self.process_messages();
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 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) }