diff --git a/arm7tdmi/src/simple_memory.rs b/arm7tdmi/src/simple_memory.rs index df0e561..4d5d986 100644 --- a/arm7tdmi/src/simple_memory.rs +++ b/arm7tdmi/src/simple_memory.rs @@ -69,13 +69,16 @@ impl DebugRead for SimpleMemory { impl MemoryGdbInterface for SimpleMemory { fn memory_map_xml(&self, offset: u64, length: usize, buf: &mut [u8]) -> usize { - let memory_map = format!(r#" + let memory_map = format!( + r#" - "#, self.data.len()); + "#, + self.data.len() + ); copy_range_to_buf(memory_map.trim().as_bytes(), offset, length, buf) } } diff --git a/core/BENCHMARK.md b/core/BENCHMARK.md index edad29b..401d0c2 100644 --- a/core/BENCHMARK.md +++ b/core/BENCHMARK.md @@ -2,5 +2,5 @@ rustboyadvance-core crate provides a simple yet effective performance benchmark. -to run it use `cargo bench --manifest-path ./core/Cargo.toml --features no_video_interface` from the repository root +to run it use `cargo bench --manifest-path ./core/Cargo.toml` from the repository root diff --git a/core/Cargo.toml b/core/Cargo.toml index e6c65d7..300c713 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -54,6 +54,4 @@ harness = false default = [] elf_support = [] debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"] -gdb = ["gdbstub"] -# For use for ports where VideoInterface is not needed like wasm & jni -no_video_interface = [] +gdb = ["gdbstub"] \ No newline at end of file diff --git a/core/src/cartridge/loader.rs b/core/src/cartridge/loader.rs index e55589e..99ce52d 100644 --- a/core/src/cartridge/loader.rs +++ b/core/src/cartridge/loader.rs @@ -11,7 +11,6 @@ use rustboyadvance_utils::elf::{load_elf, GoblinError}; use rustboyadvance_utils::read_bin_file; use zip::ZipArchive; - pub enum LoadRom { #[cfg(feature = "elf_support")] Elf { diff --git a/core/src/gba.rs b/core/src/gba.rs index 6e8b2b9..4bf0bcc 100644 --- a/core/src/gba.rs +++ b/core/src/gba.rs @@ -16,8 +16,6 @@ use super::sysbus::SysBus; use super::timer::Timers; use super::AudioInterface; -#[cfg(not(feature = "no_video_interface"))] -use super::VideoInterface; use arm7tdmi::{self, Arm7tdmiCore}; use rustboyadvance_utils::Shared; @@ -28,8 +26,6 @@ pub struct GameBoyAdvance { pub io_devs: Shared, pub scheduler: SharedScheduler, interrupt_flags: SharedInterruptFlags, - #[cfg(not(feature = "no_video_interface"))] - pub video_device: Rc>, pub audio_device: Rc>, } @@ -67,7 +63,6 @@ impl GameBoyAdvance { pub fn new( bios_rom: Box<[u8]>, gamepak: Cartridge, - #[cfg(not(feature = "no_video_interface"))] video_device: Rc>, audio_device: Rc>, ) -> GameBoyAdvance { // Warn the user if the bios is not the real one @@ -108,8 +103,6 @@ impl GameBoyAdvance { cpu, sysbus, io_devs, - #[cfg(not(feature = "no_video_interface"))] - video_device, audio_device, scheduler, interrupt_flags, @@ -124,7 +117,6 @@ impl GameBoyAdvance { savestate: &[u8], bios: Box<[u8]>, rom: Box<[u8]>, - #[cfg(not(feature = "no_video_interface"))] video_device: Rc>, audio_device: Rc>, ) -> bincode::Result { let decoded: Box = bincode::deserialize_from(savestate)?; @@ -156,8 +148,6 @@ impl GameBoyAdvance { sysbus, io_devs, interrupt_flags: interrupts, - #[cfg(not(feature = "no_video_interface"))] - video_device, audio_device, scheduler, }) @@ -308,12 +298,7 @@ impl GameBoyAdvance { let apu = &mut io.sound; Some(timers.handle_overflow_event(channel_id, event_time, apu, dmac)) } - EventType::Gpu(gpu_event) => Some(io.gpu.on_event( - gpu_event, - &mut *self.sysbus, - #[cfg(not(feature = "no_video_interface"))] - &self.video_device, - )), + EventType::Gpu(gpu_event) => Some(io.gpu.on_event(gpu_event, &mut *self.sysbus)), EventType::Apu(event) => Some(io.sound.on_event(event, &self.audio_device)), }; if let Some((new_event, when)) = new_event { @@ -380,8 +365,6 @@ impl GameBoyAdvance { breakpoint } - /// Query the emulator for the recently drawn framebuffer. - /// for use with implementations where the VideoInterface is not a viable option. pub fn get_frame_buffer(&self) -> &[u32] { self.sysbus.io.gpu.get_frame_buffer() } @@ -401,17 +384,15 @@ mod tests { use crate::cartridge::GamepakBuilder; use arm7tdmi::memory::BusIO; - struct DummyInterface {} + struct DummyAudio {} - impl DummyInterface { - fn new() -> DummyInterface { - DummyInterface {} + impl DummyAudio { + fn new() -> DummyAudio { + DummyAudio {} } } - #[cfg(not(feature = "no_video_interface"))] - impl VideoInterface for DummyInterface {} - impl AudioInterface for DummyInterface {} + impl AudioInterface for DummyAudio {} fn make_mock_gba(rom: &[u8]) -> GameBoyAdvance { let bios = vec![0; 0x4000].into_boxed_slice(); @@ -421,14 +402,8 @@ mod tests { .without_backup_to_file() .build() .unwrap(); - let dummy = Rc::new(RefCell::new(DummyInterface::new())); - let mut gba = GameBoyAdvance::new( - bios, - cartridge, - #[cfg(not(feature = "no_video_interface"))] - dummy.clone(), - dummy.clone(), - ); + let dummy = Rc::new(RefCell::new(DummyAudio::new())); + let mut gba = GameBoyAdvance::new(bios, cartridge, dummy.clone()); gba.skip_bios(); gba diff --git a/core/src/gpu/mod.rs b/core/src/gpu/mod.rs index 8ad8ac6..078704d 100644 --- a/core/src/gpu/mod.rs +++ b/core/src/gpu/mod.rs @@ -1,8 +1,3 @@ -#[cfg(not(feature = "no_video_interface"))] -use std::cell::RefCell; -#[cfg(not(feature = "no_video_interface"))] -use std::rc::Rc; - use num::FromPrimitive; use serde::{Deserialize, Serialize}; @@ -13,8 +8,6 @@ use super::dma::{DmaNotifer, TIMING_HBLANK, TIMING_VBLANK}; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::sched::{EventType, FutureEvent, GpuEvent, Scheduler}; pub use super::sysbus::consts::*; -#[cfg(not(feature = "no_video_interface"))] -use super::VideoInterface; mod render; @@ -107,9 +100,6 @@ impl Default for ObjBufferEntry { } } -#[cfg(not(feature = "no_video_interface"))] -type VideoDeviceRcRefCell = Rc>; - #[derive(Serialize, Deserialize, Clone, DebugStub)] pub struct Gpu { interrupt_flags: SharedInterruptFlags, @@ -369,11 +359,7 @@ impl Gpu { (GpuEvent::HBlank, CYCLES_HBLANK) } - fn handle_hblank_end( - &mut self, - dma_notifier: &mut D, - #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) -> FutureGpuEvent { + fn handle_hblank_end(&mut self, dma_notifier: &mut D) -> FutureGpuEvent { self.update_vcount(self.vcount + 1); if self.vcount < DISPLAY_HEIGHT { @@ -401,9 +387,6 @@ impl Gpu { dma_notifier.notify(TIMING_VBLANK); - #[cfg(not(feature = "no_video_interface"))] - video_device.borrow_mut().render(&self.frame_buffer); - self.obj_buffer_reset(); (GpuEvent::VBlankHDraw, CYCLES_HDRAW) @@ -432,22 +415,13 @@ impl Gpu { } } - pub fn on_event( - &mut self, - event: GpuEvent, - dma_notifier: &mut D, - #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) -> FutureEvent + pub fn on_event(&mut self, event: GpuEvent, dma_notifier: &mut D) -> FutureEvent where D: DmaNotifer, { let (event, when) = match event { GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier), - GpuEvent::HBlank => self.handle_hblank_end( - dma_notifier, - #[cfg(not(feature = "no_video_interface"))] - video_device, - ), + GpuEvent::HBlank => self.handle_hblank_end(dma_notifier), GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(), GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(), }; @@ -549,26 +523,10 @@ mod tests { fn notify(&mut self, _timing: u16) {} } - #[derive(Default)] - struct TestVideoInterface { - frame_counter: usize, - } - - #[cfg(not(feature = "no_video_interface"))] - impl VideoInterface for TestVideoInterface { - fn render(&mut self, _buffer: &[u32]) { - self.frame_counter += 1; - } - } - #[test] fn test_gpu_state_machine() { let mut sched = Scheduler::new(); let mut gpu = Gpu::new(&mut sched, Rc::new(Cell::new(Default::default()))); - #[cfg(not(feature = "no_video_interface"))] - let video = Rc::new(RefCell::new(TestVideoInterface::default())); - #[cfg(not(feature = "no_video_interface"))] - let video_clone: VideoDeviceRcRefCell = video.clone(); let mut dma_notifier = NopDmaNotifer; gpu.dispstat.vcount_setting = 0; @@ -580,12 +538,7 @@ mod tests { let (event, event_time) = sched.pop_pending_event().unwrap(); assert_eq!(event_time, sched.timestamp()); let next_event = match event { - EventType::Gpu(event) => gpu.on_event( - event, - &mut dma_notifier, - #[cfg(not(feature = "no_video_interface"))] - &video_clone, - ), + EventType::Gpu(event) => gpu.on_event(event, &mut dma_notifier), _ => panic!("Found unexpected event in queue!"), }; sched.schedule(next_event); @@ -594,8 +547,6 @@ mod tests { for line in 0..160 { println!("line = {}", line); - #[cfg(not(feature = "no_video_interface"))] - assert_eq!(video.borrow().frame_counter, 0); assert_eq!(gpu.vcount, line); assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw))); assert_eq!(gpu.dispstat.hblank_flag, false); @@ -613,9 +564,6 @@ mod tests { assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), false); } - #[cfg(not(feature = "no_video_interface"))] - assert_eq!(video.borrow().frame_counter, 1); - for line in 0..68 { println!("line = {}", 160 + line); assert_eq!(gpu.dispstat.hblank_flag, false); @@ -638,8 +586,6 @@ mod tests { update!(CYCLES_HBLANK); } - #[cfg(not(feature = "no_video_interface"))] - assert_eq!(video.borrow().frame_counter, 1); assert_eq!(sched.timestamp(), CYCLES_FULL_REFRESH); assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), true); diff --git a/core/src/lib.rs b/core/src/lib.rs index 471606c..fbe1f7f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -55,12 +55,6 @@ pub(crate) mod overrides; #[cfg(feature = "debugger")] pub mod debugger; -#[cfg(not(feature = "no_video_interface"))] -pub trait VideoInterface { - #[allow(unused_variables)] - fn render(&mut self, buffer: &[u32]) {} -} - pub type StereoSample = [T; 2]; pub trait AudioInterface { @@ -129,8 +123,6 @@ pub mod prelude { pub use super::debugger::Debugger; pub use super::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}; pub use super::Bus; - #[cfg(not(feature = "no_video_interface"))] - pub use super::VideoInterface; pub use super::{AudioInterface, StereoSample}; pub use super::{GBAError, GBAResult, GameBoyAdvance}; pub use rustboyadvance_utils::{read_bin_file, write_bin_file}; diff --git a/fps_bench/src/main.rs b/fps_bench/src/main.rs index 16f292b..4a36b6a 100644 --- a/fps_bench/src/main.rs +++ b/fps_bench/src/main.rs @@ -6,16 +6,15 @@ use std::rc::Rc; use rustboyadvance_core::prelude::*; use rustboyadvance_core::util::FpsCounter; -struct BenchmarkHardware {} +struct DummyAudio {} -impl BenchmarkHardware { - fn new() -> BenchmarkHardware { - BenchmarkHardware {} +impl DummyAudio { + fn new() -> DummyAudio { + DummyAudio {} } } -impl VideoInterface for BenchmarkHardware {} -impl AudioInterface for BenchmarkHardware {} +impl AudioInterface for DummyAudio {} fn main() { if env::args().count() < 3 { @@ -36,14 +35,9 @@ fn main() { .build() .unwrap(); - let dummy = Rc::new(RefCell::new(BenchmarkHardware::new())); + let dummy = Rc::new(RefCell::new(DummyAudio::new())); - let mut gba = GameBoyAdvance::new( - bios.into_boxed_slice(), - gamepak, - dummy.clone(), - dummy.clone(), - ); + let mut gba = GameBoyAdvance::new(bios.into_boxed_slice(), gamepak, dummy.clone()); gba.skip_bios(); let mut fps_counter = FpsCounter::default(); diff --git a/platform/rustboyadvance-jni/Cargo.toml b/platform/rustboyadvance-jni/Cargo.toml index 824e1d1..c4fe42b 100644 --- a/platform/rustboyadvance-jni/Cargo.toml +++ b/platform/rustboyadvance-jni/Cargo.toml @@ -10,8 +10,8 @@ publish = false crate-type = ["staticlib", "cdylib"] [dependencies] -rustboyadvance-core = {path = "../../core/", features = ["no_video_interface"]} -rustboyadvance-utils = {path = "../../utils/" } +rustboyadvance-core = { path = "../../core/" } +rustboyadvance-utils = { path = "../../utils/" } jni = "0.17.0" log = {version = "0.4.8", features = ["release_max_level_info", "max_level_debug"]} diff --git a/platform/rustboyadvance-jni/src/emulator.rs b/platform/rustboyadvance-jni/src/emulator.rs index 4d0d242..8e7646c 100644 --- a/platform/rustboyadvance-jni/src/emulator.rs +++ b/platform/rustboyadvance-jni/src/emulator.rs @@ -15,12 +15,12 @@ use jni::JNIEnv; use crate::audio::{self, connector::AudioJNIConnector, thread::AudioThreadCommand}; -struct Hardware { +struct AudioDevice { sample_rate: i32, audio_producer: Option>, } -impl AudioInterface for Hardware { +impl AudioInterface for AudioDevice { fn push_sample(&mut self, samples: &[i16]) { if let Some(prod) = &mut self.audio_producer { for s in samples.iter() { @@ -146,7 +146,7 @@ impl Default for EmulationState { } pub struct EmulatorContext { - hwif: Rc>, + audio_device: Rc>, renderer: Renderer, audio_player_ref: GlobalRef, keypad: Keypad, @@ -188,11 +188,11 @@ impl EmulatorContext { let renderer = Renderer::new(env, renderer_obj)?; info!("Creating GBA Instance"); - let hw = Rc::new(RefCell::new(Hardware { + let audio = Rc::new(RefCell::new(AudioDevice { sample_rate: audio::util::get_sample_rate(env, audio_player), audio_producer: None, })); - let mut gba = GameBoyAdvance::new(bios, gamepak, hw.clone()); + let mut gba = GameBoyAdvance::new(bios, gamepak, audio.clone()); if skip_bios != 0 { info!("skipping bios"); gba.skip_bios(); @@ -209,7 +209,7 @@ impl EmulatorContext { renderer, audio_player_ref, emustate: Mutex::new(EmulationState::default()), - hwif: hw.clone(), + audio_device: audio.clone(), }; Ok(context) } @@ -237,17 +237,18 @@ impl EmulatorContext { let renderer = Renderer::new(env, renderer_obj)?; - let hw = Rc::new(RefCell::new(Hardware { + let audio = Rc::new(RefCell::new(AudioDevice { sample_rate: audio::util::get_sample_rate(env, audio_player), audio_producer: None, })); - let gba = GameBoyAdvance::from_saved_state(&savestate, bios, rom, hw.clone()) - .map_err(|e| { - format!( - "failed to create GameBoyAdvance from saved savestate, error {:?}", - e - ) - })?; + let gba = GameBoyAdvance::from_saved_state(&savestate, bios, rom, audio.clone()).map_err( + |e| { + format!( + "failed to create GameBoyAdvance from saved savestate, error {:?}", + e + ) + }, + )?; let keypad = Keypad::new(env, keypad_obj); @@ -258,7 +259,7 @@ impl EmulatorContext { renderer, audio_player_ref, emustate: Mutex::new(EmulationState::default()), - hwif: hw.clone(), + audio_device: audio.clone(), }) } @@ -288,7 +289,7 @@ impl EmulatorContext { let (prod, cons) = AudioRingBuffer::new_with_capacity(audio_connector.sample_count).split(); // Store the ringbuffer producer in the emulator - self.hwif.borrow_mut().audio_producer = Some(prod); + self.audio_device.borrow_mut().audio_producer = Some(prod); // Spawn the audio worker thread, give it the audio connector, jvm and ringbuffer consumer let (audio_thread_handle, audio_thread_tx) = @@ -354,7 +355,7 @@ impl EmulatorContext { audio_connector.pause(env); - self.hwif.borrow_mut().audio_producer = None; + self.audio_device.borrow_mut().audio_producer = None; *self.emustate.lock().unwrap() = EmulationState::Stopped; diff --git a/platform/rustboyadvance-libretro/Cargo.toml b/platform/rustboyadvance-libretro/Cargo.toml index 68e1002..9d151fc 100644 --- a/platform/rustboyadvance-libretro/Cargo.toml +++ b/platform/rustboyadvance-libretro/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] -rustboyadvance-core = { path = "../../core/", features = ["no_video_interface"] } +rustboyadvance-core = { path = "../../core/" } rustboyadvance-utils = { path = "../../utils" } log = "0.4.8" libc = "0.2" diff --git a/platform/rustboyadvance-libretro/src/lib.rs b/platform/rustboyadvance-libretro/src/lib.rs index 2dba8e8..d1fd5a3 100644 --- a/platform/rustboyadvance-libretro/src/lib.rs +++ b/platform/rustboyadvance-libretro/src/lib.rs @@ -49,7 +49,7 @@ struct GbaButton(_GbaButton); impl Deref for GbaButton { type Target = _GbaButton; fn deref(&self) -> &Self::Target { - return &self.0 + return &self.0; } } diff --git a/platform/rustboyadvance-sdl2/src/main.rs b/platform/rustboyadvance-sdl2/src/main.rs index 801a84e..3664650 100644 --- a/platform/rustboyadvance-sdl2/src/main.rs +++ b/platform/rustboyadvance-sdl2/src/main.rs @@ -1,4 +1,3 @@ -use sdl2; use sdl2::controller::Button; use sdl2::event::{Event, WindowEvent}; use sdl2::image::{InitFlag, LoadSurface, LoadTexture}; @@ -7,6 +6,7 @@ use sdl2::pixels::Color; use sdl2::rect::Rect; use sdl2::render::WindowCanvas; use sdl2::surface::Surface; +use sdl2::{self}; use sdl2::EventPump; @@ -37,7 +37,7 @@ mod input; mod video; use audio::{create_audio_player, create_dummy_player}; -use video::{create_video_interface, SCREEN_HEIGHT, SCREEN_WIDTH}; +use video::{SCREEN_HEIGHT, SCREEN_WIDTH}; use rustboyadvance_core::cartridge::BackupType; use rustboyadvance_core::prelude::*; @@ -182,7 +182,7 @@ fn main() -> Result<(), Box> { } }; - let video = Rc::new(RefCell::new(create_video_interface(canvas))); + let mut renderer = video::Renderer::from_canvas(canvas); let audio: Rc> = if silent { Rc::new(RefCell::new(create_dummy_player())) } else { @@ -205,12 +205,7 @@ fn main() -> Result<(), Box> { let gamepak = builder.build()?; - let mut gba = GameBoyAdvance::new( - bios_bin.clone(), - gamepak, - video.clone(), - audio.clone(), - ); + let mut gba = GameBoyAdvance::new(bios_bin.clone(), gamepak, audio.clone()); if skip_bios { gba.skip_bios(); @@ -331,12 +326,7 @@ fn main() -> Result<(), Box> { let bios_bin = read_bin_file(bios_path).unwrap(); // create a new emulator - TODO, export to a function - gba = GameBoyAdvance::new( - bios_bin.into_boxed_slice(), - gamepak, - video.clone(), - audio.clone(), - ); + gba = GameBoyAdvance::new(bios_bin.into_boxed_slice(), gamepak, audio.clone()); gba.skip_bios(); } _ => {} @@ -344,10 +334,11 @@ fn main() -> Result<(), Box> { } gba.frame(); + renderer.render(gba.get_frame_buffer()); if let Some(fps) = fps_counter.tick() { let title = format!("{} ({} fps)", rom_name, fps); - video.borrow_mut().set_window_title(&title); + renderer.set_window_title(&title); } if frame_limiter { diff --git a/platform/rustboyadvance-sdl2/src/video.rs b/platform/rustboyadvance-sdl2/src/video.rs index 91370bd..45ca50f 100644 --- a/platform/rustboyadvance-sdl2/src/video.rs +++ b/platform/rustboyadvance-sdl2/src/video.rs @@ -4,25 +4,37 @@ use sdl2::render::{Texture, TextureCreator, WindowCanvas}; use sdl2::video::WindowContext; use rustboyadvance_core::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}; -use rustboyadvance_core::VideoInterface; pub const SCREEN_WIDTH: u32 = DISPLAY_WIDTH as u32; pub const SCREEN_HEIGHT: u32 = DISPLAY_HEIGHT as u32; -pub struct Sdl2Video<'a> { +pub struct Renderer<'a> { _tc: TextureCreator, // only kept alive because of the texture texture: Texture<'a>, // TODO - what happens if _tc is destroyed first ? canvas: WindowCanvas, } -impl<'a> Sdl2Video<'a> { +impl<'a> Renderer<'a> { + pub fn from_canvas(canvas: WindowCanvas) -> Renderer<'a> { + let mut tc = canvas.texture_creator(); + let texture = unsafe { + let tc_ptr = &mut tc as *mut TextureCreator; + (*tc_ptr) + .create_texture_streaming(PixelFormatEnum::BGRA32, SCREEN_WIDTH, SCREEN_HEIGHT) + .unwrap() + }; + Renderer { + _tc: tc, + texture, + canvas, + } + } + pub fn set_window_title(&mut self, title: &str) { self.canvas.window_mut().set_title(&title).unwrap(); } -} -impl<'a> VideoInterface for Sdl2Video<'a> { - fn render(&mut self, buffer: &[u32]) { + pub fn render(&mut self, buffer: &[u32]) { self.texture .update( None, @@ -42,18 +54,3 @@ impl<'a> VideoInterface for Sdl2Video<'a> { self.canvas.present(); } } - -pub fn create_video_interface<'a>(canvas: WindowCanvas) -> Sdl2Video<'a> { - let mut tc = canvas.texture_creator(); - let texture = unsafe { - let tc_ptr = &mut tc as *mut TextureCreator; - (*tc_ptr) - .create_texture_streaming(PixelFormatEnum::BGRA32, SCREEN_WIDTH, SCREEN_HEIGHT) - .unwrap() - }; - Sdl2Video { - _tc: tc, - texture: texture, - canvas: canvas, - } -} diff --git a/platform/rustboyadvance-wasm/src/emulator.rs b/platform/rustboyadvance-wasm/src/emulator.rs index 48ba0aa..70f3508 100644 --- a/platform/rustboyadvance-wasm/src/emulator.rs +++ b/platform/rustboyadvance-wasm/src/emulator.rs @@ -19,10 +19,21 @@ use bit::BitIndex; pub struct Emulator { gba: GameBoyAdvance, interface: Rc>, + frame: Option>, +} + +fn translate_frame_to_u8(input_fb: &[u32], out_fb: &mut [u8]) { + // TODO optimize + for i in 0..input_fb.len() { + let color = input_fb[i]; + out_fb[4 * i + 0] = ((color >> 16) & 0xff) as u8; + out_fb[4 * i + 1] = ((color >> 8) & 0xff) as u8; + out_fb[4 * i + 2] = (color & 0xff) as u8; + out_fb[4 * i + 3] = 255; + } } struct Interface { - frame: Vec, sample_rate: i32, audio_ctx: AudioContext, audio_ring_buffer: AudioRingBuffer, @@ -37,7 +48,6 @@ impl Drop for Interface { impl Interface { fn new(audio_ctx: AudioContext) -> Result { Ok(Interface { - frame: vec![0; 240 * 160 * 4], sample_rate: audio_ctx.sample_rate() as i32, audio_ctx: audio_ctx, audio_ring_buffer: Default::default(), @@ -45,19 +55,6 @@ impl Interface { } } -impl VideoInterface for Interface { - fn render(&mut self, buffer: &[u32]) { - // TODO optimize - for i in 0..buffer.len() { - let color = buffer[i]; - self.frame[4 * i + 0] = ((color >> 16) & 0xff) as u8; - self.frame[4 * i + 1] = ((color >> 8) & 0xff) as u8; - self.frame[4 * i + 2] = (color & 0xff) as u8; - self.frame[4 * i + 3] = 255; - } - } -} - fn convert_sample(s: i16) -> f32 { (s as f32) / 32767_f32 } @@ -88,15 +85,13 @@ impl Emulator { .build() .unwrap(); - let gba = GameBoyAdvance::new( - bios.to_vec().into_boxed_slice(), - gamepak, - interface.clone(), - interface.clone(), - interface.clone(), - ); + let gba = GameBoyAdvance::new(bios.to_vec().into_boxed_slice(), gamepak, interface.clone()); - Ok(Emulator { gba, interface }) + Ok(Emulator { + gba, + interface, + frame: Some(vec![0; 240 * 160 * 4].into_boxed_slice()), + }) } pub fn skip_bios(&mut self) { @@ -105,13 +100,11 @@ impl Emulator { pub fn run_frame(&mut self, ctx: &CanvasRenderingContext2d) -> Result<(), JsValue> { self.gba.frame(); - let mut frame_buffer = &mut self.interface.borrow_mut().frame; - let data = web_sys::ImageData::new_with_u8_clamped_array_and_sh( - Clamped(&mut frame_buffer), - 240, - 160, - ) - .unwrap(); + let mut frame = self.frame.take().unwrap(); + translate_frame_to_u8(self.gba.get_frame_buffer(), &mut frame); + let data = + web_sys::ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut frame), 240, 160)?; + self.frame.replace(frame); ctx.put_image_data(&data, 0.0, 0.0) } @@ -134,14 +127,14 @@ impl Emulator { pub fn key_down(&mut self, event_key: &str) { debug!("Key down: {}", event_key); if let Some(key) = Emulator::map_key(event_key) { - self.gba.get_key_state().set_bit(key as usize, false); + self.gba.get_key_state_mut().set_bit(key as usize, false); } } pub fn key_up(&mut self, event_key: &str) { debug!("Key up: {}", event_key); if let Some(key) = Emulator::map_key(event_key) { - self.gba.get_key_state().set_bit(key as usize, true); + self.gba.get_key_state_mut().set_bit(key as usize, true); } }