diff --git a/gb-emu/src/main.rs b/gb-emu/src/main.rs index 3c97201..da8fa61 100644 --- a/gb-emu/src/main.rs +++ b/gb-emu/src/main.rs @@ -12,14 +12,13 @@ use gb_emu_lib::{ }, EmulatorCore, }; -use gilrs::Gilrs; use serde::{Deserialize, Serialize}; use std::{ path::PathBuf, sync::{mpsc::channel, OnceLock}, time::{Duration, Instant}, }; -use window::{WindowManager, WindowRenderer}; +use window::WindowManager; mod audio; #[cfg(feature = "camera")] @@ -253,26 +252,16 @@ fn run(args: Args) -> ! { let mut window_manager = WindowManager::new(sender, stream, args.record); - let window = window_manager.add_main( - scale_override, - Some(Gilrs::new().unwrap()), - shader_path, - resizable, - ); + let window = window_manager.add_main(scale_override, shader_path, resizable); - let tile_window: Option = if args.tile_window { - Some(window_manager.add(configs.standalone_config.scale_factor, None, None, false)) + let tile_window = if args.tile_window { + Some(window_manager.add(configs.standalone_config.scale_factor, None, false)) } else { None }; - let layer_window: Option = if args.layer_window { - Some(window_manager.add( - configs.standalone_config.scale_factor.min(2), - None, - None, - false, - )) + let layer_window = if args.layer_window { + Some(window_manager.add(configs.standalone_config.scale_factor.min(2), None, false)) } else { None }; diff --git a/gb-emu/src/window.rs b/gb-emu/src/window.rs index d3944e6..7a4c587 100644 --- a/gb-emu/src/window.rs +++ b/gb-emu/src/window.rs @@ -1,18 +1,18 @@ use std::{ collections::HashMap, path::PathBuf, - sync::{mpsc::Sender, Arc, Mutex, RwLock}, + sync::{ + mpsc::{self, Receiver, Sender}, + Arc, + }, }; use cpal::Stream; use gb_emu_lib::{ - connect::{EmulatorMessage, JoypadState, Renderer, ResolutionData}, + connect::{EmulatorMessage, JoypadState, RendererMessage, ResolutionData}, renderer::{RendererBackend, RendererBackendManager, WindowOptions}, }; -use gilrs::{ - ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks}, - Button, Gilrs, -}; +use gilrs::{Button, Gilrs}; use image::ImageBuffer; #[cfg(feature = "vulkan")] use raw_window_handle::HasRawDisplayHandle; @@ -27,17 +27,7 @@ use winit_input_helper::WinitInputHelper; use crate::access_config; -pub struct WindowInfo { - id: WindowId, - data: Arc, -} - -struct WindowData { - renderer: Mutex, - window: Window, - rendered_size: RwLock<(u32, u32)>, - last_buf: Mutex>, -} +type RendererChannel = Sender>; pub struct WindowManager { event_loop: EventLoop<()>, @@ -47,10 +37,12 @@ pub struct WindowManager { struct WindowManagerData { main_window: Option, - windows: HashMap>, + windows: HashMap, window_data_manager: Arc, - input: Arc>, + input: WinitInputHelper, sender: Sender, + gamepad_handler: Gilrs, + joypad_state: JoypadState, _stream: Stream, } @@ -68,9 +60,10 @@ impl WindowManager { main_window: None, windows: HashMap::new(), window_data_manager, - input: Arc::new(Mutex::new(WinitInputHelper::new())), + input: WinitInputHelper::new(), sender, - + gamepad_handler: Gilrs::new().unwrap(), + joypad_state: Default::default(), _stream, }, record_main, @@ -80,13 +73,11 @@ impl WindowManager { pub(crate) fn add_main( &mut self, factor: usize, - gamepad_handler: Option, shader_path: Option, resizable: bool, - ) -> WindowRenderer { + ) -> RendererChannel { self.data.add( factor, - gamepad_handler, shader_path, resizable, &self.event_loop, @@ -98,13 +89,11 @@ impl WindowManager { pub(crate) fn add( &mut self, factor: usize, - gamepad_handler: Option, shader_path: Option, resizable: bool, - ) -> WindowRenderer { + ) -> RendererChannel { self.data.add( factor, - gamepad_handler, shader_path, resizable, &self.event_loop, @@ -119,7 +108,7 @@ impl WindowManager { }); } - pub fn run_events_blocking(self) -> ! { + pub fn run_events_blocking(mut self) -> ! { self.event_loop.run(move |event, target, control_flow| { self.data.handler(false, event, target, control_flow) }) @@ -127,88 +116,78 @@ impl WindowManager { } impl WindowManagerData { - #[allow(clippy::too_many_arguments)] fn add( &mut self, factor: usize, - gamepad_handler: Option, shader_path: Option, resizable: bool, event_loop: &EventLoop<()>, is_main: bool, record: bool, - ) -> WindowRenderer { - let (r, info) = WindowRenderer::new( + ) -> RendererChannel { + let (r, sender) = WindowRenderer::new( factor, - gamepad_handler, - self.input.clone(), event_loop, self.window_data_manager.clone(), shader_path, resizable, record, ); - self.windows.insert(info.id, info.data); + let id = r.window.id(); + self.windows.insert(id, r); if is_main { - self.main_window = Some(info.id); + self.main_window = Some(id); } - r + sender } fn handler( - &self, + &mut self, run_return: bool, event: Event<'_, ()>, _target: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow, ) { - control_flow.set_wait(); + control_flow.set_poll(); - if let Ok(mut i) = self.input.lock() { - if i.update(&event) && i.key_pressed(VirtualKeyCode::Space) { - if let Some(window) = self - .main_window - .as_ref() - .and_then(|id| self.windows.get(id)) - { - if let Ok(buf) = window.last_buf.lock() { - if let Ok(size) = window.rendered_size.read() { - let image = ImageBuffer::, _>::from_raw( - size.0, - size.1, - bytemuck::cast_slice(buf.as_ref()), - ) - .unwrap(); - let configs = access_config(); - let screenshot_dir = - if configs.standalone_config.group_screenshots_by_rom { - configs - .config_dir - .join(format!("screenshots/{}", configs.rom_title)) - } else { - configs.config_dir.clone() - }; + if self.input.update(&event) && self.input.key_pressed(VirtualKeyCode::Space) { + if let Some(window) = self + .main_window + .as_ref() + .and_then(|id| self.windows.get(id)) + { + let image = ImageBuffer::, _>::from_raw( + window.width as u32, + window.height as u32, + bytemuck::cast_slice(&window.queued_buf.buf), + ) + .unwrap(); + let configs = access_config(); + let screenshot_dir = if configs.standalone_config.group_screenshots_by_rom { + configs + .config_dir + .join(format!("screenshots/{}", configs.rom_title)) + } else { + configs.config_dir.clone() + }; - std::fs::create_dir_all(&screenshot_dir) - .expect("could not create screenshot directory!"); + std::fs::create_dir_all(&screenshot_dir) + .expect("could not create screenshot directory!"); - let screenshot_path = screenshot_dir.join(format!( - "{} - {}.png", - chrono::DateTime::::from( - std::time::SystemTime::now() - ) - .to_rfc3339(), - configs.rom_title, - )); - image - .save(screenshot_path) - .expect("Could not save screenshot!"); - } - } - } + let screenshot_path = screenshot_dir.join(format!( + "{} - {}.png", + chrono::DateTime::::from(std::time::SystemTime::now()) + .to_rfc3339(), + configs.rom_title, + )); + image + .save(screenshot_path) + .expect("Could not save screenshot!"); } } + self.process_input(); + match event { Event::Resumed => { self.sender.send(EmulatorMessage::Start).unwrap(); @@ -228,62 +207,108 @@ impl WindowManagerData { } } Event::MainEventsCleared => { + for window in self.windows.values_mut() { + window.process(); + // if window.is_prepared { + window.render(&self.window_data_manager); + // } + } + 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, - ); - } - } + if let Some(w) = self.windows.get_mut(&window_id) { + w.render(&self.window_data_manager); } } _ => {} } } + + fn process_input(&mut self) { + self.joypad_state.reset(); + + while let Some(event) = self.gamepad_handler.next_event() { + if let gilrs::EventType::ButtonPressed(button, _) = event.event { + match button { + Button::DPadDown => self.joypad_state.down = true, + Button::DPadUp => self.joypad_state.up = true, + Button::DPadLeft => self.joypad_state.left = true, + Button::DPadRight => self.joypad_state.right = true, + Button::Start => self.joypad_state.start = true, + Button::Select => self.joypad_state.select = true, + Button::East => self.joypad_state.a = true, + Button::South => self.joypad_state.b = true, + _ => {} + } + } + + for (_id, pad) in self.gamepad_handler.gamepads() { + self.joypad_state.down |= pad.is_pressed(Button::DPadDown); + self.joypad_state.up |= pad.is_pressed(Button::DPadUp); + self.joypad_state.left |= pad.is_pressed(Button::DPadLeft); + self.joypad_state.right |= pad.is_pressed(Button::DPadRight); + self.joypad_state.start |= pad.is_pressed(Button::Start); + self.joypad_state.select |= pad.is_pressed(Button::Select); + self.joypad_state.a |= pad.is_pressed(Button::East); + self.joypad_state.b |= pad.is_pressed(Button::South); + } + } + + self.joypad_state.down |= + self.input.key_held(VirtualKeyCode::Down) || self.input.key_held(VirtualKeyCode::S); + self.joypad_state.up |= + self.input.key_held(VirtualKeyCode::Up) || self.input.key_held(VirtualKeyCode::W); + self.joypad_state.left |= + self.input.key_held(VirtualKeyCode::Left) || self.input.key_held(VirtualKeyCode::A); + self.joypad_state.right |= + self.input.key_held(VirtualKeyCode::Right) || self.input.key_held(VirtualKeyCode::D); + self.joypad_state.start |= self.input.key_held(VirtualKeyCode::Equals); + self.joypad_state.select |= self.input.key_held(VirtualKeyCode::Minus); + self.joypad_state.a |= self.input.key_held(VirtualKeyCode::Apostrophe); + self.joypad_state.b |= self.input.key_held(VirtualKeyCode::Semicolon); + + self.sender + .send(EmulatorMessage::JoypadUpdate(self.joypad_state)) + .expect("error sending joypad state"); + } } pub struct WindowRenderer { - data: Arc, - input: Arc>, + receiver: Receiver>, + renderer: RendererBackend, + window: Window, width: usize, height: usize, factor: usize, - gamepad_handler: Option, - joypad_state: JoypadState, - current_rumble: bool, + queued_buf: QueuedBuf, recording: Option, } -struct RecordInfo { - dir: PathBuf, - frame_num: usize, +#[derive(Default)] +struct QueuedBuf { + buf: Vec<[u8; 4]>, + displayed: bool, +} + +impl QueuedBuf { + fn update(&mut self, new: Vec<[u8; 4]>) { + self.buf = new; + self.displayed = false; + } } impl WindowRenderer { - #[allow(clippy::too_many_arguments, unused_variables)] - pub fn new( + fn new( factor: usize, - gamepad_handler: Option, - input: Arc>, event_loop: &EventLoop<()>, manager: Arc, shader_path: Option, resizable: bool, record: bool, - ) -> (Self, WindowInfo) { + ) -> (Self, RendererChannel) { let window = WindowBuilder::new() .with_title("Gameboy") .with_resizable(resizable) @@ -304,20 +329,7 @@ impl WindowRenderer { #[cfg(feature = "pixels")] let options = WindowOptions {}; - let renderer = Mutex::new(RendererBackend::new(resolutions, &window, options, manager)); - let id = window.id(); - - let data = Arc::new(WindowData { - renderer, - window, - rendered_size: RwLock::new((1, 1)), - last_buf: Mutex::new(Vec::new()), - }); - - let info = WindowInfo { - id, - data: data.clone(), - }; + let renderer = RendererBackend::new(resolutions, &window, options, manager); let recording = if record { let configs = access_config(); @@ -335,39 +347,63 @@ impl WindowRenderer { None }; + let (sender, receiver) = mpsc::channel(); + ( Self { - data, - input, - width: 0, - height: 0, + receiver, + renderer, + window, + width: 1, + height: 1, factor, - gamepad_handler, - joypad_state: JoypadState::default(), - current_rumble: false, + queued_buf: Default::default(), recording, }, - info, + sender, ) } -} -impl Renderer<[u8; 4]> for WindowRenderer { + fn render(&mut self, manager: &RendererBackendManager) { + let inner_size = self.window.inner_size(); + if !self.queued_buf.displayed { + self.renderer.new_frame(&self.queued_buf.buf); + self.queued_buf.displayed = true; + } + + self.renderer.render( + ResolutionData { + real_width: inner_size.width, + real_height: inner_size.height, + scaled_width: self.width as u32, + scaled_height: self.height as u32, + }, + manager, + ); + } + + fn process(&mut self) { + while let Ok(message) = self.receiver.try_recv() { + match message { + RendererMessage::Prepare { width, height } => self.prepare(width, height), + RendererMessage::Resize { width, height } => self.prepare(width, height), + RendererMessage::Display { buffer } => self.display(buffer), + RendererMessage::SetTitle { title } => self.window.set_title(&title), + RendererMessage::Rumble { rumble: _ } => todo!(), + } + } + } + fn prepare(&mut self, width: usize, height: usize) { self.width = width; self.height = height; - let real_factor = (self.data.window.scale_factor() * self.factor as f64) as u32; + let real_factor = (self.window.scale_factor() * self.factor as f64) as u32; let real_width = (width as u32) * real_factor; let real_height = (height as u32) * real_factor; - if let Ok(mut f) = self.data.rendered_size.write() { - *f = (width as u32, height as u32); - } - - self.data - .window + self.window .set_inner_size(PhysicalSize::new(real_width, real_height)); let resolutions = ResolutionData { @@ -377,27 +413,21 @@ impl Renderer<[u8; 4]> for WindowRenderer { scaled_height: height as u32, }; - if let Ok(mut data) = self.data.renderer.lock() { - data.resize(resolutions, &self.data.window); - } - self.data.window.request_redraw(); + self.renderer.resize(resolutions, &self.window); + + self.window.request_redraw(); } - fn display(&mut self, buffer: &[[u8; 4]]) { - if let Ok(mut data) = self.data.renderer.lock() { - data.new_frame(buffer); - } - self.data.window.request_redraw(); - if let Ok(mut last_buf) = self.data.last_buf.lock() { - last_buf.resize(buffer.len(), [0; 4]); - last_buf.copy_from_slice(buffer); - } + fn display(&mut self, buffer: Vec<[u8; 4]>) { + self.queued_buf.update(buffer); + + self.window.request_redraw(); if let Some(ref mut info) = self.recording { let image = ImageBuffer::, _>::from_raw( self.width as u32, self.height as u32, - bytemuck::cast_slice(buffer), + bytemuck::cast_slice(&self.queued_buf.buf), ) .unwrap(); @@ -407,87 +437,39 @@ impl Renderer<[u8; 4]> for WindowRenderer { info.frame_num += 1; } } - - fn set_title(&mut self, title: String) { - self.data.window.set_title(&title); - } - - fn latest_joypad_state(&mut self) -> JoypadState { - self.joypad_state.reset(); - - if let Some(ref mut gamepad_handler) = self.gamepad_handler { - while let Some(event) = gamepad_handler.next_event() { - if let gilrs::EventType::ButtonPressed(button, _) = event.event { - match button { - Button::DPadDown => self.joypad_state.down = true, - Button::DPadUp => self.joypad_state.up = true, - Button::DPadLeft => self.joypad_state.left = true, - Button::DPadRight => self.joypad_state.right = true, - Button::Start => self.joypad_state.start = true, - Button::Select => self.joypad_state.select = true, - Button::East => self.joypad_state.a = true, - Button::South => self.joypad_state.b = true, - _ => {} - } - } - } - - for (_, pad) in gamepad_handler.gamepads() { - self.joypad_state.down |= pad.is_pressed(Button::DPadDown); - self.joypad_state.up |= pad.is_pressed(Button::DPadUp); - self.joypad_state.left |= pad.is_pressed(Button::DPadLeft); - self.joypad_state.right |= pad.is_pressed(Button::DPadRight); - self.joypad_state.start |= pad.is_pressed(Button::Start); - self.joypad_state.select |= pad.is_pressed(Button::Select); - self.joypad_state.a |= pad.is_pressed(Button::East); - self.joypad_state.b |= pad.is_pressed(Button::South); - } - } - - if let Ok(input) = self.input.lock() { - self.joypad_state.down |= - input.key_held(VirtualKeyCode::Down) || input.key_held(VirtualKeyCode::S); - self.joypad_state.up |= - input.key_held(VirtualKeyCode::Up) || input.key_held(VirtualKeyCode::W); - self.joypad_state.left |= - input.key_held(VirtualKeyCode::Left) || input.key_held(VirtualKeyCode::A); - self.joypad_state.right |= - input.key_held(VirtualKeyCode::Right) || input.key_held(VirtualKeyCode::D); - self.joypad_state.start |= input.key_held(VirtualKeyCode::Equals); - self.joypad_state.select |= input.key_held(VirtualKeyCode::Minus); - self.joypad_state.a |= input.key_held(VirtualKeyCode::Apostrophe); - self.joypad_state.b |= input.key_held(VirtualKeyCode::Semicolon); - } - self.joypad_state - } - - fn set_rumble(&mut self, rumbling: bool) { - if rumbling != self.current_rumble && let Some(ref mut gamepad_handler) = self.gamepad_handler { - self.current_rumble = rumbling; - - let ids = gamepad_handler - .gamepads() - .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None }) - .collect::>(); - if ids.is_empty() { - return; - } - - let magnitude = if rumbling { 0xFF } else { 0x0 }; - - EffectBuilder::new() - .add_effect(BaseEffect { - kind: BaseEffectType::Strong { magnitude }, - scheduling: Replay { - after: Ticks::from_ms(0), - play_for: Ticks::from_ms(16), - with_delay: Ticks::from_ms(0), - }, - envelope: Default::default(), - }) - .gamepads(&ids) - .finish(gamepad_handler) - .unwrap(); - } - } } + +struct RecordInfo { + dir: PathBuf, + frame_num: usize, +} + +// fn set_rumble(&mut self, rumbling: bool) { +// if rumbling != self.current_rumble && let Some(ref mut gamepad_handler) = self.gamepad_handler { +// self.current_rumble = rumbling; + +// let ids = gamepad_handler +// .gamepads() +// .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None }) +// .collect::>(); +// if ids.is_empty() { +// return; +// } + +// let magnitude = if rumbling { 0xFF } else { 0x0 }; + +// EffectBuilder::new() +// .add_effect(BaseEffect { +// kind: BaseEffectType::Strong { magnitude }, +// scheduling: Replay { +// after: Ticks::from_ms(0), +// play_for: Ticks::from_ms(16), +// with_delay: Ticks::from_ms(0), +// }, +// envelope: Default::default(), +// }) +// .gamepads(&ids) +// .finish(gamepad_handler) +// .unwrap(); +// } +// } diff --git a/gb-vst/src/lib.rs b/gb-vst/src/lib.rs index 5ee8b97..d80e8cb 100644 --- a/gb-vst/src/lib.rs +++ b/gb-vst/src/lib.rs @@ -5,7 +5,7 @@ use gb_emu_lib::{ config::ConfigManager, connect::{ AudioOutput, CgbRomType, DownsampleType, EmulatorCoreTrait, EmulatorMessage, - EmulatorOptions, JoypadButtons, NoCamera, RomFile, SerialTarget, + EmulatorOptions, NoCamera, RendererMessage, RomFile, SerialTarget, }, renderer::RendererBackendManager, EmulatorCore, HEIGHT, WIDTH, @@ -21,7 +21,7 @@ use std::{ Arc, Mutex, OnceLock, RwLock, }, }; -use ui::{Emulator, EmulatorRenderer}; +use ui::Emulator; mod ui; @@ -53,11 +53,15 @@ struct EmuParams { struct EmuVars { rx: AsyncHeapConsumer<[f32; 2]>, - sender: Sender, - emulator_core: EmulatorCore<[u8; 4], EmulatorRenderer, NoCamera>, + emulator_core: EmulatorCore<[u8; 4], NoCamera>, serial_tx: Sender, } +struct EmuComms { + sender: Sender, + receiver: Receiver>, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct VstConfig { @@ -106,18 +110,11 @@ fn access_config<'a>() -> &'a Configs { #[derive(Default)] pub struct GameboyEmu { vars: Option, - frame_receiver: Arc, - key_handler: Arc, + emu_comms: Arc>>, params: Arc, renderer_manager: Arc>>>, } -type Frame = Vec<[u8; 4]>; -type JoypadInfo = (JoypadButtons, bool); - -type FrameReceiver = Mutex>>; -type JoypadSender = Mutex>>; - const BUFFERS_PER_FRAME: usize = 1; const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::Linear; @@ -259,8 +256,7 @@ impl Plugin for GameboyEmu { ); Some(Box::new(Emulator::new( - self.frame_receiver.clone(), - self.key_handler.clone(), + self.emu_comms.clone(), self.renderer_manager.clone(), size, ))) @@ -303,10 +299,12 @@ impl Plugin for GameboyEmu { DOWNSAMPLE_TYPE, ); - let (window, frame_receiver, key_handler) = EmulatorRenderer::new(); + let (emu_sender, renderer_receiver) = mpsc::channel(); - *self.frame_receiver.lock().unwrap() = Some(frame_receiver); - *self.key_handler.lock().unwrap() = Some(key_handler); + *self.emu_comms.lock().unwrap() = Some(EmuComms { + sender, + receiver: renderer_receiver, + }); let (serial_tx, gb_serial_rx) = mpsc::channel::(); let serial_target = SerialTarget::Custom { @@ -321,7 +319,7 @@ impl Plugin for GameboyEmu { let options = EmulatorOptions::new_with_config( configs.emu_config.clone(), configs.config_dir.clone(), - window, + emu_sender, rom, output, ) @@ -336,7 +334,6 @@ impl Plugin for GameboyEmu { self.vars = Some(EmuVars { rx, - sender, emulator_core, serial_tx, }); @@ -346,10 +343,12 @@ impl Plugin for GameboyEmu { } fn deactivate(&mut self) { - if let Some(ref mut vars) = self.vars { - match vars.sender.send(EmulatorMessage::Exit) { - Ok(_) => self.vars = None, - Err(e) => nih_log!("error {e} sending message to emulator"), + if let Ok(comms) = self.emu_comms.lock() { + if let Some(ref comms) = *comms { + match comms.sender.send(EmulatorMessage::Exit) { + Ok(_) => self.vars = None, + Err(e) => nih_log!("error {e} sending message to emulator"), + } } } } diff --git a/gb-vst/src/ui.rs b/gb-vst/src/ui.rs index 7282e4a..5046b95 100644 --- a/gb-vst/src/ui.rs +++ b/gb-vst/src/ui.rs @@ -1,38 +1,32 @@ use std::{ path::PathBuf, - sync::{ - mpsc::{self, Receiver, Sender}, - Arc, Mutex, - }, + sync::{Arc, Mutex}, }; use baseview::{Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowOpenOptions}; use gb_emu_lib::{ - connect::{JoypadButtons, JoypadState, Renderer, ResolutionData, HEIGHT, WIDTH}, + connect::{JoypadButtons, JoypadState, RendererMessage, ResolutionData, HEIGHT, WIDTH}, renderer::{RendererBackend, RendererBackendManager, WindowOptions}, }; use keyboard_types::{Code, KeyState}; use nih_plug::prelude::*; -use crate::{access_config, Frame, FrameReceiver, JoypadInfo, JoypadSender, IS_CGB}; +use crate::{access_config, EmuComms, IS_CGB}; pub struct Emulator { - frame_receiver: Arc, - joypad_sender: Arc, + emu_comms: Arc>>, manager: Arc>>>, size: Size, } impl Emulator { pub fn new( - frame_receiver: Arc, - joypad_sender: Arc, + emu_comms: Arc>>, manager: Arc>>>, size: Size, ) -> Self { Self { - frame_receiver, - joypad_sender, + emu_comms, manager, size, } @@ -45,8 +39,7 @@ impl Editor for Emulator { parent: ParentWindowHandle, _context: Arc, ) -> Box { - let fr_cloned = self.frame_receiver.clone(); - let js_cloned = self.joypad_sender.clone(); + let rr_cloned = self.emu_comms.clone(); // let (size, scale) = if cfg!(target_os = "macos") { // ( @@ -89,8 +82,7 @@ impl Editor for Emulator { move |w| { EmulatorWindow::new( w, - fr_cloned, - js_cloned, + rr_cloned, m, size, #[cfg(feature = "vulkan")] @@ -100,8 +92,7 @@ impl Editor for Emulator { }, ); Box::new(Self::new( - self.frame_receiver.clone(), - self.joypad_sender.clone(), + self.emu_comms.clone(), self.manager.clone(), size, )) @@ -125,16 +116,16 @@ impl Editor for Emulator { pub struct EmulatorWindow { renderer: RendererBackend, manager: Arc, - frame_receiver: Arc, - joypad_sender: Arc, + emu_comms: Arc>>, + joypad_state: JoypadState, + latest_buf: Vec<[u8; 4]>, current_resolution: ResolutionData, } impl EmulatorWindow { fn new( window: &mut Window, - frame_receiver: Arc, - joypad_sender: Arc, + emu_comms: Arc>>, manager: Arc>>>, size: Size, #[cfg(feature = "vulkan")] shader_path: Option, @@ -164,21 +155,47 @@ impl EmulatorWindow { Self { renderer, manager: m.clone(), - frame_receiver, - joypad_sender, + emu_comms, + joypad_state: Default::default(), + latest_buf: Vec::new(), current_resolution, }, m.clone(), ) } + + fn process_events(&mut self) { + if let Ok(comms) = self.emu_comms.lock() { + if let Some(ref comms) = *comms { + while let Ok(e) = comms.receiver.try_recv() { + match e { + RendererMessage::Prepare { + width: _, + height: _, + } => {} + RendererMessage::Resize { + width: _, + height: _, + } => {} + RendererMessage::Display { buffer } => self.latest_buf = buffer, + RendererMessage::SetTitle { title: _ } => {} + RendererMessage::Rumble { rumble: _ } => {} + } + } + } + } + } } impl WindowHandler for EmulatorWindow { fn on_frame(&mut self, _window: &mut Window) { - if let Some(ref mut receiver) = *self.frame_receiver.lock().expect("failed to lock mutex") { - if let Some(ref buf) = receiver.try_iter().last() { - self.renderer.new_frame(buf); - } + self.process_events(); + + if self.latest_buf.len() + == (self.current_resolution.scaled_height * self.current_resolution.scaled_width) + as usize + { + self.renderer.new_frame(&self.latest_buf); } self.renderer.render(self.current_resolution, &self.manager); @@ -210,10 +227,18 @@ impl WindowHandler for EmulatorWindow { Code::KeyD | Code::ArrowRight => Some(JoypadButtons::Right), _ => None, } { - if let Some(ref mut sender) = - *self.joypad_sender.lock().expect("failed to lock mutex") - { - sender.send((button, status)).unwrap(); + self.joypad_state.set(button, status); + if let Ok(comms) = self.emu_comms.lock() { + if let Some(ref comms) = *comms { + match comms.sender.send( + gb_emu_lib::connect::EmulatorMessage::JoypadUpdate( + self.joypad_state, + ), + ) { + Ok(_) => {} + Err(e) => println!("error sending joypad update: {e:#?}"), + } + } } EventStatus::Captured } else { @@ -224,49 +249,3 @@ impl WindowHandler for EmulatorWindow { } } } - -pub struct EmulatorRenderer { - tx: Sender, - joypad: JoypadState, - keys: Receiver, -} - -impl EmulatorRenderer { - pub(super) fn new() -> (Self, Receiver, Sender) { - let (tx, rx) = mpsc::channel::(); - let (keys_tx, keys) = mpsc::channel::(); - ( - Self { - tx, - joypad: JoypadState::default(), - keys, - }, - rx, - keys_tx, - ) - } -} - -impl Renderer<[u8; 4]> for EmulatorRenderer { - fn prepare(&mut self, _width: usize, _height: usize) {} - - fn display(&mut self, buffer: &[[u8; 4]]) { - let _ = self.tx.send(buffer.to_vec()); - } - - fn latest_joypad_state(&mut self) -> JoypadState { - while let Ok((key, state)) = self.keys.try_recv() { - match key { - JoypadButtons::Down => self.joypad.down = state, - JoypadButtons::Up => self.joypad.up = state, - JoypadButtons::Left => self.joypad.left = state, - JoypadButtons::Right => self.joypad.right = state, - JoypadButtons::Start => self.joypad.start = state, - JoypadButtons::Select => self.joypad.select = state, - JoypadButtons::B => self.joypad.b = state, - JoypadButtons::A => self.joypad.a = state, - } - } - self.joypad - } -} diff --git a/lib/src/connect/mod.rs b/lib/src/connect/mod.rs index b064517..530bbac 100644 --- a/lib/src/connect/mod.rs +++ b/lib/src/connect/mod.rs @@ -1,6 +1,7 @@ use std::fs; use std::marker::PhantomData; use std::path::PathBuf; +use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex, RwLock}; pub use crate::processor::memory::mmio::gpu::Colour; @@ -17,6 +18,7 @@ pub enum EmulatorMessage { Start, Pause, Exit, + JoypadUpdate(JoypadState), } #[derive(Clone, Copy)] @@ -74,20 +76,18 @@ impl RomFile { } } -pub trait Renderer> { - fn prepare(&mut self, width: usize, height: usize); +pub enum RendererMessage> { + Prepare { width: usize, height: usize }, + Resize { width: usize, height: usize }, + Display { buffer: Vec }, + SetTitle { title: String }, + Rumble { rumble: bool }, +} - fn resize(&mut self, width: usize, height: usize) { - self.prepare(width, height) +impl> RendererMessage { + pub fn display_message(buffer: Vec) -> Self { + Self::Display { buffer } } - - fn display(&mut self, buffer: &[Format]); - - fn set_title(&mut self, _title: String) {} - - fn latest_joypad_state(&mut self) -> JoypadState; - - fn set_rumble(&mut self, _rumbling: bool) {} } #[cfg(any(feature = "vulkan-renderer", feature = "pixels-renderer"))] @@ -220,15 +220,14 @@ pub enum SramType { } #[non_exhaustive] -pub struct EmulatorOptions +pub struct EmulatorOptions where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { - pub(crate) window: R, - pub(crate) tile_window: Option, - pub(crate) layer_window: Option, + pub(crate) window: Sender>, + pub(crate) tile_window: Option>>, + pub(crate) layer_window: Option>>, pub(crate) rom: Rom, pub(crate) output: AudioOutput, pub(crate) save: Option, @@ -242,13 +241,16 @@ where _spooky: PhantomData, } -impl EmulatorOptions +impl EmulatorOptions where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { - pub fn new(window: R, rom: Rom, output: AudioOutput) -> Self { + pub fn new( + window: Sender>, + rom: Rom, + output: AudioOutput, + ) -> Self { Self { window, tile_window: None, @@ -270,7 +272,7 @@ where pub fn new_with_config( config: crate::config::Config, config_dir: PathBuf, - window: R, + window: Sender>, rom: Rom, output: AudioOutput, ) -> Self { @@ -337,12 +339,18 @@ where self } - pub fn with_tile_window(mut self, window: Option) -> Self { + pub fn with_tile_window( + mut self, + window: Option>>, + ) -> Self { self.tile_window = window; self } - pub fn with_layer_window(mut self, window: Option) -> Self { + pub fn with_layer_window( + mut self, + window: Option>>, + ) -> Self { self.layer_window = window; self } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 73dab24..fd1a082 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -3,7 +3,7 @@ use crate::processor::{memory::Memory, Flags}; use connect::{ AudioOutput, CameraWrapper, EmulatorCoreTrait, EmulatorMessage, EmulatorOptions, PocketCamera, - Renderer, RomFile, + RomFile, }; use processor::{ memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets}, @@ -34,28 +34,26 @@ pub mod util; pub const WIDTH: usize = 160; pub const HEIGHT: usize = 144; -pub struct EmulatorCore +pub struct EmulatorCore where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { receiver: Receiver, - cpu: Cpu, + cpu: Cpu, paused: bool, spooky: PhantomData, } -impl EmulatorCore +impl EmulatorCore where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub fn init( paused: bool, receiver: Receiver, - mut options: EmulatorOptions, + options: EmulatorOptions, camera: Arc>>, ) -> Self { let rom = options.rom; @@ -73,13 +71,24 @@ where .load_data() .expect("Error loading bootrom!"); - options.window.prepare(WIDTH, HEIGHT); - options.window.set_title(format!( - "{} on {} on {}", - rom.get_title(), - rom.mbc_type(), - if is_cgb_mode { "CGB" } else { "DMG" } - )); + options + .window + .send(connect::RendererMessage::Prepare { + width: WIDTH, + height: HEIGHT, + }) + .expect("message error"); + options + .window + .send(connect::RendererMessage::SetTitle { + title: format!( + "{} on {} on {}", + rom.get_title(), + rom.mbc_type(), + if is_cgb_mode { "CGB" } else { "DMG" } + ), + }) + .expect("message error"); Self::new( paused, @@ -103,11 +112,7 @@ where ) } - fn new( - paused: bool, - receiver: Receiver, - cpu: Cpu, - ) -> Self { + fn new(paused: bool, receiver: Receiver, cpu: Cpu) -> Self { Self { receiver, cpu, @@ -166,14 +171,16 @@ where } EmulatorMessage::Start => self.paused = false, EmulatorMessage::Pause => self.paused = true, + EmulatorMessage::JoypadUpdate(new_state) => { + self.cpu.next_joypad_state = Some(new_state) + } } } } -impl EmulatorCoreTrait for EmulatorCore +impl EmulatorCoreTrait for EmulatorCore where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { fn replace_output(&mut self, new: AudioOutput) { diff --git a/lib/src/processor/instructions/mod.rs b/lib/src/processor/instructions/mod.rs index 89713b9..1d2f8d4 100644 --- a/lib/src/processor/instructions/mod.rs +++ b/lib/src/processor/instructions/mod.rs @@ -1,15 +1,14 @@ pub mod primitives; use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::{memory::mmio::gpu::Colour, Cpu, Direction, Flags, Reg8, SplitRegister}, util::{clear_bit, get_bit, set_bit}, }; -impl Cpu +impl Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn and(&mut self, first: u8, second: u8) -> u8 { diff --git a/lib/src/processor/instructions/primitives.rs b/lib/src/processor/instructions/primitives.rs index d45db0b..c58a6a1 100644 --- a/lib/src/processor/instructions/primitives.rs +++ b/lib/src/processor/instructions/primitives.rs @@ -1,14 +1,13 @@ use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::{memory::mmio::gpu::Colour, Cpu, Direction, Flags, SplitRegister}, util::{as_signed, get_bit, get_rotation_carry, rotate, Nibbles}, }; use std::ops::{BitAnd, BitOr}; -impl Cpu +impl Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn pop_word(&mut self) -> u16 { diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index ded6ebb..d4dd1ee 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, sync::mpsc::Sender}; pub use self::rom::Rom; use self::{ @@ -10,7 +10,9 @@ use self::{ }, }; use crate::{ - connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget}, + connect::{ + AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, RendererMessage, SerialTarget, + }, Cpu, }; @@ -85,10 +87,9 @@ struct CgbPeripherals { double_speed: DoubleSpeed, } -pub struct Memory +pub struct Memory where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { bootrom: Option>, @@ -101,7 +102,7 @@ where pub(super) user_mode: bool, oam_dma: OamDma, joypad: Joypad, - gpu: Gpu, + gpu: Gpu, apu: Apu, serial: Serial, timers: Timer, @@ -109,33 +110,31 @@ where cgb_peripherals: Option, } -pub(crate) struct OutputTargets +pub(crate) struct OutputTargets where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { - window: R, + window: Sender>, audio: AudioOutput, serial_target: SerialTarget, - tile_window: Option, - layer_window: Option, + tile_window: Option>>, + layer_window: Option>>, camera: CameraWrapperRef, _phantom: PhantomData, } -impl OutputTargets +impl OutputTargets where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn new( - window: R, + window: Sender>, audio: AudioOutput, serial_target: SerialTarget, - tile_window: Option, - layer_window: Option, + tile_window: Option>>, + layer_window: Option>>, camera: CameraWrapperRef, ) -> Self { Self { @@ -150,17 +149,16 @@ where } } -impl Memory +impl Memory where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn init( cgb: bool, bootrom: Vec, rom: Rom, - output: OutputTargets, + output: OutputTargets, ) -> Self { Self { bootrom: Some(bootrom), @@ -255,7 +253,12 @@ where self.rom.set(address, data); if self.rom.can_rumble() { // rumble - self.gpu.window.set_rumble(self.rom.is_rumbling()) + self.gpu + .window + .send(RendererMessage::Rumble { + rumble: self.rom.is_rumbling(), + }) + .expect("message error"); } } Address::Vram(address) => self.gpu.set_vram(address, data), @@ -416,10 +419,9 @@ where } } -impl Cpu +impl Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub fn increment_timers(&mut self, machine_cycles: usize) { @@ -465,15 +467,17 @@ where .interrupts .set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat); - if gpu_interrupts.vblank { - self.memory - .interrupts - .set_interrupt(Interrupt::Vblank, true); - let latest_state = self.memory.gpu.window.latest_joypad_state(); - let joypad_interrupt = self.memory.update_pressed_keys(latest_state); + if let Some(next_joypad_state) = self.next_joypad_state.take() { + let joypad_interrupt = self.memory.update_pressed_keys(next_joypad_state); self.memory .interrupts .set_interrupt(Interrupt::Joypad, joypad_interrupt); } + + if gpu_interrupts.vblank { + self.memory + .interrupts + .set_interrupt(Interrupt::Vblank, true); + } } } diff --git a/lib/src/processor/memory/mmio/cgb/double_speed.rs b/lib/src/processor/memory/mmio/cgb/double_speed.rs index 28f3eec..fd5bad3 100644 --- a/lib/src/processor/memory/mmio/cgb/double_speed.rs +++ b/lib/src/processor/memory/mmio/cgb/double_speed.rs @@ -1,5 +1,5 @@ use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::memory::{mmio::gpu::Colour, Memory}, }; use serde::{Deserialize, Serialize}; @@ -20,10 +20,9 @@ impl DoubleSpeed { } } -impl Memory +impl Memory where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn is_double_speed(&self) -> bool { diff --git a/lib/src/processor/memory/mmio/cgb/vram_dma.rs b/lib/src/processor/memory/mmio/cgb/vram_dma.rs index 801c4a6..e1184d8 100644 --- a/lib/src/processor/memory/mmio/cgb/vram_dma.rs +++ b/lib/src/processor/memory/mmio/cgb/vram_dma.rs @@ -1,5 +1,5 @@ use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::{ memory::{ addresses::{AddressMarker, VramDmaAddress}, @@ -80,10 +80,9 @@ impl VramDma { } } -impl Memory +impl Memory where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn vram_dma_tick(&mut self) -> usize { diff --git a/lib/src/processor/memory/mmio/gpu.rs b/lib/src/processor/memory/mmio/gpu.rs index 9327f90..6aef66a 100644 --- a/lib/src/processor/memory/mmio/gpu.rs +++ b/lib/src/processor/memory/mmio/gpu.rs @@ -1,3 +1,5 @@ +use std::sync::mpsc::Sender; + pub(crate) use self::types::DrawMode; use self::{ cgb::CgbData, @@ -8,7 +10,7 @@ use self::{ }, }; use crate::{ - connect::Renderer, + connect::RendererMessage, processor::memory::addresses::{OamAddress, VramAddress}, util::{clear_bit, get_bit}, HEIGHT, WIDTH, @@ -23,15 +25,14 @@ mod types; const TILE_WINDOW_WIDTH: usize = 16 * 8; const TILE_WINDOW_HEIGHT: usize = 24 * 8; -pub struct Gpu +pub struct Gpu where ColourFormat: From + Copy, - R: Renderer, { pub buffer: [ColourFormat; WIDTH * HEIGHT], pub vram: Vram, pub oam: Oam, - pub window: R, + pub window: Sender>, is_bg_zero: [bool; WIDTH], is_bg_priority: [bool; WIDTH], lcdc: Lcdc, @@ -39,8 +40,11 @@ where mode_clock: usize, scanline: u8, lyc: u8, - tile_window: Option>, - layer_window: Option<(R, [ColourFormat; WIDTH * HEIGHT * 3])>, + tile_window: Option>, + layer_window: Option<( + Sender>, + [ColourFormat; WIDTH * HEIGHT * 3], + )>, window_lc: u8, has_window_been_enabled: bool, bg_palette: Palette, @@ -54,24 +58,28 @@ where cgb_data: Option, } -impl Gpu +impl Gpu where ColourFormat: From + Copy, - R: Renderer, { pub fn new( cgb: bool, - window: R, - tile_window_renderer: Option, - layer_window_renderer: Option, + window: Sender>, + tile_window_renderer: Option>>, + layer_window_renderer: Option>>, ) -> Self { let tile_window = tile_window_renderer .map(|tile_window_renderer| TileWindow::new(tile_window_renderer, cgb)); let buffer = [get_blank_colour(cgb); WIDTH * HEIGHT]; - let layer_window = layer_window_renderer.map(|mut layer_window_renderer| { - layer_window_renderer.prepare(WIDTH, HEIGHT * 3); + let layer_window = layer_window_renderer.map(|layer_window_renderer| { + layer_window_renderer + .send(RendererMessage::Prepare { + width: WIDTH, + height: HEIGHT * 3, + }) + .expect("message error"); ( layer_window_renderer, [get_blank_colour(cgb); WIDTH * HEIGHT * 3], @@ -484,9 +492,18 @@ where } fn render_window(&mut self) { - self.window.display(&self.buffer); + let mut buffer = Vec::new(); + buffer.extend_from_slice(&self.buffer); + self.window + .send(RendererMessage::display_message(buffer)) + .expect("message error"); + if let Some((ref mut window, ref mut buffer)) = self.layer_window { - window.display(buffer); + let mut new_buffer = Vec::new(); + new_buffer.extend_from_slice(buffer); + window + .send(RendererMessage::display_message(new_buffer)) + .expect("message error"); for val in buffer { *val = get_blank_colour(self.cgb_data.is_some()); diff --git a/lib/src/processor/memory/mmio/gpu/addresses.rs b/lib/src/processor/memory/mmio/gpu/addresses.rs index 1f8ab3c..b319937 100644 --- a/lib/src/processor/memory/mmio/gpu/addresses.rs +++ b/lib/src/processor/memory/mmio/gpu/addresses.rs @@ -1,17 +1,13 @@ -use crate::{ - connect::Renderer, - util::{get_bit, set_or_clear_bit}, -}; +use crate::util::{get_bit, set_or_clear_bit}; use super::{ types::{DrawMode, ObjSize, Palette, TiledataArea, TilemapArea}, Colour, Gpu, }; -impl Gpu +impl Gpu where ColourFormat: From + Copy, - R: Renderer, { pub fn update_lcdc(&mut self, data: u8) { self.lcdc.enable = get_bit(data, 7); diff --git a/lib/src/processor/memory/mmio/gpu/cgb.rs b/lib/src/processor/memory/mmio/gpu/cgb.rs index df53a7a..e0300b3 100644 --- a/lib/src/processor/memory/mmio/gpu/cgb.rs +++ b/lib/src/processor/memory/mmio/gpu/cgb.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; use crate::{ - connect::Renderer, processor::memory::addresses::{AddressMarker, CgbPaletteAddress}, util::get_bit, }; @@ -78,10 +77,9 @@ impl CgbPalette { } } -impl Gpu +impl Gpu where ColourFormat: From + Copy, - R: Renderer, { pub(crate) fn get_cgb_palette(&self, address: CgbPaletteAddress) -> u8 { if let Some(cgb_data) = &self.cgb_data { diff --git a/lib/src/processor/memory/mmio/gpu/tile_window.rs b/lib/src/processor/memory/mmio/gpu/tile_window.rs index 9ccf6a1..22c291e 100644 --- a/lib/src/processor/memory/mmio/gpu/tile_window.rs +++ b/lib/src/processor/memory/mmio/gpu/tile_window.rs @@ -1,5 +1,7 @@ +use std::sync::mpsc::Sender; + use crate::{ - connect::Renderer, + connect::RendererMessage, processor::memory::mmio::gpu::{Palette, TILE_WINDOW_HEIGHT, TILE_WINDOW_WIDTH}, util::get_bit, }; @@ -9,28 +11,31 @@ use super::{ Colour, }; -pub(super) struct TileWindow +pub(super) struct TileWindow where ColourFormat: From + Copy, - R: Renderer, { sprite_buffer: Vec, - sprite_renderer: R, + sprite_renderer: Sender>, currently_cgb: bool, } -impl TileWindow +impl TileWindow where ColourFormat: From + Copy, - R: Renderer, { - pub(super) fn new(mut window: R, cgb: bool) -> Self { + pub(super) fn new(window: Sender>, cgb: bool) -> Self { let current_width = if cgb { TILE_WINDOW_WIDTH * 2 } else { TILE_WINDOW_WIDTH }; - window.prepare(current_width, TILE_WINDOW_HEIGHT); + window + .send(RendererMessage::Prepare { + width: current_width, + height: TILE_WINDOW_HEIGHT, + }) + .expect("message error"); Self { sprite_buffer: vec![ ColourInner::Error.rgb_bytes(None).into(); @@ -55,7 +60,11 @@ where TILE_WINDOW_WIDTH }; self.sprite_renderer - .resize(current_width, TILE_WINDOW_HEIGHT); + .send(RendererMessage::Resize { + width: current_width, + height: TILE_WINDOW_HEIGHT, + }) + .expect("message error"); self.sprite_buffer = vec![ColourInner::Error.rgb_bytes(None).into(); current_width * TILE_WINDOW_HEIGHT]; } @@ -63,7 +72,9 @@ where self.draw_row(tile_y, data_begin, palette, memory, is_cgb_mode); } - self.sprite_renderer.display(&self.sprite_buffer); + self.sprite_renderer + .send(RendererMessage::display_message(self.sprite_buffer.clone())) + .expect("message error"); } fn draw_row( diff --git a/lib/src/processor/memory/mmio/joypad.rs b/lib/src/processor/memory/mmio/joypad.rs index 2a761e3..729509b 100644 --- a/lib/src/processor/memory/mmio/joypad.rs +++ b/lib/src/processor/memory/mmio/joypad.rs @@ -44,6 +44,19 @@ impl JoypadState { self.a = false; self.b = false; } + + pub fn set(&mut self, button: JoypadButtons, state: bool) { + *match button { + JoypadButtons::Down => &mut self.down, + JoypadButtons::Up => &mut self.up, + JoypadButtons::Left => &mut self.left, + JoypadButtons::Right => &mut self.right, + JoypadButtons::Start => &mut self.start, + JoypadButtons::Select => &mut self.select, + JoypadButtons::B => &mut self.b, + JoypadButtons::A => &mut self.a, + } = state; + } } impl Joypad { diff --git a/lib/src/processor/memory/mmio/oam_dma.rs b/lib/src/processor/memory/mmio/oam_dma.rs index cb6f8a8..660bcc9 100644 --- a/lib/src/processor/memory/mmio/oam_dma.rs +++ b/lib/src/processor/memory/mmio/oam_dma.rs @@ -1,6 +1,6 @@ use super::gpu::Colour; use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::{memory::Memory, SplitRegister}, }; use serde::{Deserialize, Serialize}; @@ -35,10 +35,9 @@ impl OamDma { } } -impl Memory +impl Memory where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub(crate) fn oam_dma_tick(&mut self, steps: usize) { diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index e13723e..d51b369 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use self::memory::{mmio::gpu::Colour, Interrupt, Memory}; -use crate::connect::{PocketCamera, Renderer}; +use crate::connect::{JoypadState, PocketCamera}; mod instructions; pub mod memory; @@ -20,13 +20,12 @@ pub(crate) enum Direction { Right, } -pub struct Cpu +pub struct Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { - pub memory: Memory, + pub memory: Memory, pub reg: Registers, pub last_instruction: u8, pub last_instruction_addr: u16, @@ -34,15 +33,15 @@ where should_halt_bug: bool, pub(super) cycle_count: usize, pub(crate) is_skipping: bool, + pub(super) next_joypad_state: Option, } -impl Cpu +impl Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { - pub(crate) fn new(memory: Memory, run_bootrom: bool) -> Self { + pub(crate) fn new(memory: Memory, run_bootrom: bool) -> Self { Self { memory, reg: Registers::init(), @@ -52,6 +51,7 @@ where should_halt_bug: false, cycle_count: 0, is_skipping: !run_bootrom, + next_joypad_state: None, } } diff --git a/lib/src/processor/opcodes.rs b/lib/src/processor/opcodes.rs index 3169170..5dab66d 100644 --- a/lib/src/processor/opcodes.rs +++ b/lib/src/processor/opcodes.rs @@ -1,5 +1,5 @@ use crate::{ - connect::{PocketCamera, Renderer}, + connect::PocketCamera, processor::{ instructions::{res, set}, Cpu, Flags, Reg8, SplitRegister, @@ -9,10 +9,9 @@ use crate::{ use super::memory::mmio::gpu::Colour; -impl Cpu +impl Cpu where ColourFormat: From + Copy, - R: Renderer, C: PocketCamera + Send + 'static, { pub fn run_opcode(&mut self, opcode: u8) -> usize { diff --git a/lib/src/renderer/vulkan/vulkan.rs b/lib/src/renderer/vulkan/vulkan.rs index 72a395a..c875446 100644 --- a/lib/src/renderer/vulkan/vulkan.rs +++ b/lib/src/renderer/vulkan/vulkan.rs @@ -225,6 +225,7 @@ struct VulkanWindowInner { swapchain: SwapchainData, surface: SurfaceData, framebuffers: FramebufferData, + image_slice: Align, frame_counter: usize, } @@ -435,6 +436,22 @@ impl VulkanWindowInner { .device .create_graphics_pipelines(vk::PipelineCache::null(), &[graphic_pipeline_infos], None) .unwrap(); + + let image_ptr = vulkan_data + .device + .map_memory( + swapchain.shader_input_image_buffer_memory, + 0, + swapchain.shader_input_image_buffer_memory_req.size, + vk::MemoryMapFlags::empty(), + ) + .unwrap(); + let image_slice: Align = Align::new( + image_ptr, + std::mem::align_of::() as u64, + swapchain.shader_input_image_buffer_memory_req.size, + ); + Self { vertex_input_buffer, renderpass, @@ -446,6 +463,7 @@ impl VulkanWindowInner { surface, framebuffers, vulkan_data, + image_slice, frame_counter: 0, } } @@ -464,35 +482,8 @@ impl VulkanWindowInner { } unsafe fn new_frame(&mut self, buffer: &[[u8; 4]]) { - let image_ptr = self - .vulkan_data - .device - .map_memory( - self.swapchain.shader_input_image_buffer_memory, - 0, - self.swapchain.shader_input_image_buffer_memory_req.size, - vk::MemoryMapFlags::empty(), - ) - .unwrap(); - let mut image_slice: Align = Align::new( - image_ptr, - std::mem::align_of::() as u64, - self.swapchain.shader_input_image_buffer_memory_req.size, - ); - - image_slice.copy_from_slice(bytemuck::cast_slice(buffer)); - - self.vulkan_data - .device - .unmap_memory(self.swapchain.shader_input_image_buffer_memory); - self.vulkan_data - .device - .bind_buffer_memory( - self.swapchain.shader_input_image_buffer, - self.swapchain.shader_input_image_buffer_memory, - 0, - ) - .unwrap(); + self.image_slice + .copy_from_slice(bytemuck::cast_slice(buffer)); record_submit_commandbuffer( &self.vulkan_data.device,