Get rid of the VideoInterface trait
Former-commit-id: 30a14ff0609b385b80b4c17a8f70e89fce816509 Former-commit-id: 519aeea19b3faa37b732463ecfdddb9730322021
This commit is contained in:
parent
0f64f07133
commit
4db32b1af2
|
@ -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#"<?xml version="1.0"?>
|
||||
let memory_map = format!(
|
||||
r#"<?xml version="1.0"?>
|
||||
<!DOCTYPE memory-map
|
||||
PUBLIC "+//IDN gnu.org//DTD GDB Memory Map V1.0//EN"
|
||||
"http://sourceware.org/gdb/gdb-memory-map.dtd">
|
||||
<memory-map>
|
||||
<memory type="ram" start="0x0" length="{}"/>
|
||||
</memory-map>"#, self.data.len());
|
||||
</memory-map>"#,
|
||||
self.data.len()
|
||||
);
|
||||
copy_range_to_buf(memory_map.trim().as_bytes(), offset, length, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"]
|
|
@ -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 {
|
||||
|
|
|
@ -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<IoDevices>,
|
||||
pub scheduler: SharedScheduler,
|
||||
interrupt_flags: SharedInterruptFlags,
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
pub video_device: Rc<RefCell<dyn VideoInterface>>,
|
||||
pub audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||
}
|
||||
|
||||
|
@ -67,7 +63,6 @@ impl GameBoyAdvance {
|
|||
pub fn new(
|
||||
bios_rom: Box<[u8]>,
|
||||
gamepak: Cartridge,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: Rc<RefCell<dyn VideoInterface>>,
|
||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||
) -> 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<RefCell<dyn VideoInterface>>,
|
||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||
) -> bincode::Result<GameBoyAdvance> {
|
||||
let decoded: Box<SaveState> = 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
|
||||
|
|
|
@ -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<RefCell<dyn VideoInterface>>;
|
||||
|
||||
#[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<D: DmaNotifer>(
|
||||
&mut self,
|
||||
dma_notifier: &mut D,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
|
||||
) -> FutureGpuEvent {
|
||||
fn handle_hblank_end<D: DmaNotifer>(&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<D>(
|
||||
&mut self,
|
||||
event: GpuEvent,
|
||||
dma_notifier: &mut D,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
|
||||
) -> FutureEvent
|
||||
pub fn on_event<D>(&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);
|
||||
|
|
|
@ -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> = [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};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"]}
|
||||
|
||||
|
|
|
@ -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<Producer<i16>>,
|
||||
}
|
||||
|
||||
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<RefCell<Hardware>>,
|
||||
audio_device: Rc<RefCell<AudioDevice>>,
|
||||
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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<dyn std::error::Error>> {
|
|||
}
|
||||
};
|
||||
|
||||
let video = Rc::new(RefCell::new(create_video_interface(canvas)));
|
||||
let mut renderer = video::Renderer::from_canvas(canvas);
|
||||
let audio: Rc<RefCell<dyn AudioInterface>> = if silent {
|
||||
Rc::new(RefCell::new(create_dummy_player()))
|
||||
} else {
|
||||
|
@ -205,12 +205,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
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<dyn std::error::Error>> {
|
|||
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<dyn std::error::Error>> {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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<WindowContext>, // 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<WindowContext>;
|
||||
(*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<WindowContext>;
|
||||
(*tc_ptr)
|
||||
.create_texture_streaming(PixelFormatEnum::BGRA32, SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||
.unwrap()
|
||||
};
|
||||
Sdl2Video {
|
||||
_tc: tc,
|
||||
texture: texture,
|
||||
canvas: canvas,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,21 @@ use bit::BitIndex;
|
|||
pub struct Emulator {
|
||||
gba: GameBoyAdvance,
|
||||
interface: Rc<RefCell<Interface>>,
|
||||
frame: Option<Box<[u8]>>,
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
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<Interface, JsValue> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue