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