change messages / add emulator pause / refactor threading for gb_emu
This commit is contained in:
parent
7cfe34fe82
commit
0a66d2cb86
7 changed files with 248 additions and 212 deletions
|
@ -105,7 +105,7 @@ impl Debugger {
|
|||
return;
|
||||
}
|
||||
}
|
||||
self.core.run();
|
||||
self.core.run(1);
|
||||
}
|
||||
|
||||
fn should_pause(&mut self) -> bool {
|
||||
|
|
|
@ -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) -> ! {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
Loading…
Add table
Reference in a new issue