From 3fcaca465412f5f02bba481c3773c486e925db55 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Tue, 7 Mar 2023 21:45:11 +1100 Subject: [PATCH] holy shit it works --- gb-vst/src/lib.rs | 17 ++++--- gb-vst/src/ui.rs | 125 +++++++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/gb-vst/src/lib.rs b/gb-vst/src/lib.rs index d64f412..3a2918e 100644 --- a/gb-vst/src/lib.rs +++ b/gb-vst/src/lib.rs @@ -6,16 +6,13 @@ use gb_emu_lib::{ }; use nih_plug::prelude::*; use std::sync::{ - mpsc::{channel, Sender}, - Arc, + mpsc::{channel, Receiver, Sender}, + Arc, Mutex, }; use ui::{Emulator, EmulatorRenderer}; mod ui; -const WIDTH: u32 = 320; -const HEIGHT: u32 = 240; - #[derive(Params, Default)] struct EmuParams {} @@ -28,6 +25,7 @@ struct EmuVars { #[derive(Default)] pub struct GameboyEmu { vars: Option, + frame_receiver: Arc>>>>, } const ROM: &[u8; 32768] = include_bytes!("../../test-roms/Tetris.gb"); @@ -89,7 +87,7 @@ impl Plugin for GameboyEmu { } fn editor(&self, _: AsyncExecutor) -> Option> { - Some(Box::new(Emulator::new())) + Some(Box::new(Emulator::new(self.frame_receiver.clone()))) } fn initialize( @@ -111,9 +109,12 @@ impl Plugin for GameboyEmu { let (output, rx) = AudioOutput::new_unfilled(buffer_config.sample_rate); - let renderer = Box::::default(); + let (renderer, frame_receiver) = EmulatorRenderer::new(); - let mut emulator_core = EmulatorCore::init(receiver, options, renderer, output, None); + *self.frame_receiver.lock().unwrap() = Some(frame_receiver); + + let mut emulator_core = + EmulatorCore::init(receiver, options, Box::new(renderer), output, None); emulator_core.run_until_buffer_full(); self.vars = Some(EmuVars { diff --git a/gb-vst/src/ui.rs b/gb-vst/src/ui.rs index 876316d..42e5bfe 100644 --- a/gb-vst/src/ui.rs +++ b/gb-vst/src/ui.rs @@ -1,20 +1,25 @@ -use std::sync::Arc; +use std::sync::{ + mpsc::{self, Receiver, Sender}, + Arc, Mutex, +}; use baseview::{ - Event, EventStatus, MouseButton, MouseEvent, Size, Window, WindowEvent, WindowHandler, - WindowInfo, WindowOpenOptions, + Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, +}; +use gb_emu_lib::{ + connect::{Renderer, HEIGHT, WIDTH}, + util::scale_buffer, }; -use gb_emu_lib::connect::Renderer; use nih_plug::prelude::*; use pixels::{Pixels, SurfaceTexture}; -use crate::{HEIGHT, WIDTH}; - -pub struct Emulator {} +pub struct Emulator { + frame_receiver: Arc>>>>, +} impl Emulator { - pub fn new() -> Self { - Self {} + pub fn new(frame_receiver: Arc>>>>) -> Self { + Self { frame_receiver } } } @@ -24,6 +29,7 @@ impl Editor for Emulator { parent: ParentWindowHandle, _context: Arc, ) -> Box { + let cloned = self.frame_receiver.clone(); Window::open_parented( &parent, WindowOpenOptions { @@ -32,13 +38,13 @@ impl Editor for Emulator { scale: baseview::WindowScalePolicy::SystemScaleFactor, gl_config: None, }, - EmulatorWindow::new, + |w| EmulatorWindow::new(w, cloned), ); - Box::new(Self::new()) + Box::new(Self::new(self.frame_receiver.clone())) } fn size(&self) -> (u32, u32) { - (WIDTH, HEIGHT) + (WIDTH as u32, HEIGHT as u32) } fn set_scale_factor(&self, _factor: f32) -> bool { @@ -54,84 +60,87 @@ impl Editor for Emulator { pub struct EmulatorWindow { pix: Pixels, - current_colour: [u8; 4], + scale: usize, + frame_receiver: Arc>>>>, } impl EmulatorWindow { - fn new(window: &mut Window) -> Self { + fn new(window: &mut Window, frame_receiver: Arc>>>>) -> Self { let info = WindowInfo::from_logical_size(Size::new(WIDTH as f64, HEIGHT as f64), 1.); - let mut pix = init_pixbuf(info, window); - - let current_colour: [u8; 4] = [0xFF, 0x0, 0x0, 0xFF]; - - for pixel in pix.get_frame_mut().chunks_exact_mut(4) { - pixel.copy_from_slice(¤t_colour); - } + let (pix, scale) = init_pixbuf(info, window); Self { pix, - current_colour, - } - } - - fn update_pixbuf(&mut self) { - self.current_colour = rotate_colour(&self.current_colour); - self.render_pixbuf(); - } - - fn render_pixbuf(&mut self) { - for pixel in self.pix.get_frame_mut().chunks_exact_mut(4) { - pixel.copy_from_slice(&self.current_colour); + scale, + frame_receiver, } } } -fn init_pixbuf(info: WindowInfo, window: &mut Window) -> Pixels { +fn init_pixbuf(info: WindowInfo, window: &mut Window) -> (Pixels, usize) { let physical_size = info.physical_size(); - pixels::Pixels::new( - physical_size.width, - physical_size.height, - SurfaceTexture::new(physical_size.width, physical_size.height, window), + let scale = (physical_size.width as usize / WIDTH).min(physical_size.height as usize / HEIGHT); + ( + pixels::Pixels::new( + physical_size.width, + physical_size.height, + SurfaceTexture::new(physical_size.width, physical_size.height, window), + ) + .unwrap(), + scale, ) - .unwrap() -} - -fn rotate_colour(current: &[u8; 4]) -> [u8; 4] { - [current[1], current[2], current[0], current[3]] } impl WindowHandler for EmulatorWindow { fn on_frame(&mut self, _window: &mut Window) { - self.pix.render().unwrap(); + if let Some(ref mut receiver) = *self.frame_receiver.lock().expect("failed to lock mutex") { + match receiver.recv() { + Ok(buf) => { + let scaled_buf = if self.scale != 1 { + scale_buffer(&buf, WIDTH, HEIGHT, self.scale) + } else { + buf + }; + for (pixel, source) in + self.pix.get_frame_mut().chunks_exact_mut(4).zip(scaled_buf) + { + pixel.copy_from_slice(&source.to_be_bytes()); + } + self.pix.render().unwrap(); + } + Err(e) => nih_log!("recv error: {e}"), + } + } } fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus { if let Event::Window(WindowEvent::Resized(info)) = event { - self.pix = init_pixbuf(info, window); - self.update_pixbuf(); - return EventStatus::Captured; - } - if let Event::Mouse(MouseEvent::ButtonPressed { - button: MouseButton::Left, - .. - }) = event - { - nih_log!("nih click!"); - self.update_pixbuf(); + (self.pix, self.scale) = init_pixbuf(info, window); return EventStatus::Captured; } EventStatus::Ignored } } -#[derive(Default)] -pub struct EmulatorRenderer {} +pub struct EmulatorRenderer { + tx: Sender>, +} + +impl EmulatorRenderer { + pub(super) fn new() -> (Self, Receiver>) { + let (tx, rx) = mpsc::channel::>(); + (Self { tx }, rx) + } +} impl Renderer for EmulatorRenderer { fn prepare(&mut self, _width: usize, _height: usize) {} - fn display(&mut self, _buffer: &[u32]) {} + #[allow(unused_must_use)] + fn display(&mut self, buffer: &[u32]) { + self.tx.send(buffer.to_vec()); + } fn latest_joypad_state(&mut self) -> gb_emu_lib::connect::JoypadState { gb_emu_lib::connect::JoypadState::default()