Compare commits

..

No commits in common. "259503e505caa9ac15c1cc92e2976ac6de378e7f" and "ac4446b0551353f0f8bb00c27ca316d645c91791" have entirely different histories.

15 changed files with 621 additions and 932 deletions

1391
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -21,11 +21,11 @@ nokhwa = { version = "0.10", features = [
"input-avfoundation", "input-avfoundation",
], optional = true } ], optional = true }
send_wrapper = { version = "0.6.0", optional = true } send_wrapper = { version = "0.6.0", optional = true }
winit = { version = "0.29.15", features = ["rwh_05"] } winit = { version = "0.29", features = ["rwh_05"] }
winit_input_helper = "0.16" winit_input_helper = "0.15"
raw-window-handle = { workspace = true } raw-window-handle = { workspace = true }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
image = { version = "0.25.1", default-features = false, features = ["png"] } image = { version = "0.24", default-features = false, features = ["png"] }
bytemuck = "1.14" bytemuck = "1.14"
chrono = "0.4" chrono = "0.4"
log = { workspace = true } log = { workspace = true }

View file

@ -3,7 +3,7 @@ use cpal::{
Stream, Stream,
}; };
use futures::executor; use futures::executor;
use gb_emu_lib::connect::{AsyncConsumer, AudioOutput, DownsampleType}; use gb_emu_lib::connect::{AudioOutput, DownsampleType};
use crate::access_config; use crate::access_config;

View file

@ -27,7 +27,7 @@ nih_plug = { workspace = true, features = [
"vst3", "vst3",
], optional = true } ], optional = true }
baseview = { workspace = true, optional = true } baseview = { workspace = true, optional = true }
async-ringbuf = { version = "0.2.1", optional = true } async-ringbuf = { version = "0.1", optional = true }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
keyboard-types = { version = "0.6.2", optional = true } keyboard-types = { version = "0.6.2", optional = true }
raw-window-handle = { workspace = true } raw-window-handle = { workspace = true }

View file

@ -1,7 +1,4 @@
use async_ringbuf::{ use async_ringbuf::AsyncHeapConsumer;
traits::{AsyncConsumer, Observer},
AsyncHeapCons,
};
use baseview::Size; use baseview::Size;
use futures::executor; use futures::executor;
use gb_emu_lib::{ use gb_emu_lib::{
@ -54,7 +51,7 @@ struct EmuParams {
} }
struct EmuVars { struct EmuVars {
rx: AsyncHeapCons<[f32; 2]>, rx: AsyncHeapConsumer<[f32; 2]>,
emulator_core: EmulatorCore<[u8; 4]>, emulator_core: EmulatorCore<[u8; 4]>,
serial_tx: Sender<u8>, serial_tx: Sender<u8>,
} }
@ -327,7 +324,6 @@ impl Plugin for GameboyEmu {
.with_show_bootrom(!will_skip_bootrom); .with_show_bootrom(!will_skip_bootrom);
EmulatorCore::init(false, receiver, options) EmulatorCore::init(false, receiver, options)
.expect("couldn't initialize emulator core!")
}; };
emulator_core.run_until_buffer_full(); emulator_core.run_until_buffer_full();

View file

@ -9,19 +9,19 @@ identifier = "com.alexjanka.TWINC.gui"
osx_file_extensions = [[["Game Boy ROM", "Viewer"], ["gb", "gbc"]]] osx_file_extensions = [[["Game Boy ROM", "Viewer"], ["gb", "gbc"]]]
[features] [features]
default = ["macos-ui", "crossplatform-ui"] default = ["macos-ui", "crossplatform-ui", "force-crossplatform-ui"]
macos-ui = ["cacao", "objc", "uuid"] macos-ui = ["cacao", "objc", "uuid"]
crossplatform-ui = ["gtk", "adw", "glib-build-tools"] crossplatform-ui = ["gtk", "adw", "glib-build-tools"]
force-crossplatform-ui = ["crossplatform-ui"] force-crossplatform-ui = ["crossplatform-ui"]
[dependencies] [dependencies]
adw = { version = "0.7.0", package = "libadwaita", features = [ adw = { version = "0.6.0", package = "libadwaita", features = [
"v1_4", "v1_4",
"gtk_v4_6", "gtk_v4_12",
], optional = true } ], optional = true }
frontend-common = { workspace = true } frontend-common = { workspace = true }
gb-emu-lib = { workspace = true } gb-emu-lib = { workspace = true }
gtk = { version = "0.9.0", package = "gtk4", features = [ gtk = { version = "0.8.0", package = "gtk4", features = [
"v4_12", "v4_12",
], optional = true } ], optional = true }
twinc_emu_vst = { path = "../gb-vst", default-features = false } twinc_emu_vst = { path = "../gb-vst", default-features = false }
@ -40,4 +40,4 @@ uuid = { version = "1.6", features = ["v4", "fast-rng"], optional = true }
[build-dependencies] [build-dependencies]
glib-build-tools = { version = "0.20.0", optional = true } glib-build-tools = { version = "0.19.0", optional = true }

View file

@ -301,14 +301,7 @@ impl CacaoWindowManager {
if state { if state {
let is_running = self.is_emulator_running(); let is_running = self.is_emulator_running();
if is_running { if is_running {
let new_layer_window: Sender<RendererMessage<[u8; 4]>> = let new_layer_window = new_layer_window(self);
match new_layer_window(self) {
Ok(t) => t,
Err(e) => {
log::error!("couldn't create tile window: {e:?}");
return;
}
};
if let Some(ref handles) = self.handles { if let Some(ref handles) = self.handles {
handles handles
.sender .sender
@ -323,13 +316,7 @@ impl CacaoWindowManager {
if state { if state {
let is_running = self.is_emulator_running(); let is_running = self.is_emulator_running();
if is_running { if is_running {
let new_tile_window = match new_tile_window(self) { let new_tile_window = new_tile_window(self);
Ok(t) => t,
Err(e) => {
log::error!("couldn't create tile window: {e:?}");
return;
}
};
if let Some(ref handles) = self.handles { if let Some(ref handles) = self.handles {
handles handles
.sender .sender

View file

@ -170,13 +170,7 @@ impl Dispatcher for TwincUiApp {
let (output, stream) = audio::create_output(false); let (output, stream) = audio::create_output(false);
let mut window_manager = self.current_game.write().unwrap(); let mut window_manager = self.current_game.write().unwrap();
window_manager.update_handles(sender, stream); window_manager.update_handles(sender, stream);
let mut core = match frontend_common::run(prepared, &mut *window_manager, output) { let mut core = frontend_common::run(prepared, &mut *window_manager, output);
Ok(c) => c,
Err(e) => {
log::error!("couldn't create emulator core: {e:?}");
return;
}
};
let handle = std::thread::Builder::new() let handle = std::thread::Builder::new()
.name(String::from("EmuCore")) .name(String::from("EmuCore"))
.spawn(move || loop { .spawn(move || loop {

View file

@ -4,7 +4,6 @@ use std::path::PathBuf;
use cacao::button::Button; use cacao::button::Button;
use cacao::filesystem::FileSelectPanel; use cacao::filesystem::FileSelectPanel;
use cacao::image::{Image, ImageView};
use cacao::input::TextField; use cacao::input::TextField;
use cacao::layout::{Layout, LayoutConstraint}; use cacao::layout::{Layout, LayoutConstraint};
use cacao::select::Select; use cacao::select::Select;
@ -206,7 +205,7 @@ pub struct PickerView<T>
where where
T: ToString, T: ToString,
{ {
pub _view: View, pub view: View,
pub select: Select, pub select: Select,
pub title: Label, pub title: Label,
_p: PhantomData<T>, _p: PhantomData<T>,
@ -244,7 +243,7 @@ where
]); ]);
Self { Self {
_view: view, view,
select, select,
title, title,
_p: PhantomData, _p: PhantomData,
@ -486,43 +485,3 @@ impl StepperViewToggle {
self.enabled = !self.enabled; self.enabled = !self.enabled;
} }
} }
pub struct ImageViewWrapper {
view: View,
image_view: ImageView,
_image: Image,
}
impl ImageViewWrapper {
pub fn new(image: Image) -> Self {
let view = View::new();
let image_view = ImageView::new();
image_view.set_image(&image);
view.add_subview(&image_view);
LayoutConstraint::activate(&[
image_view
.leading
.constraint_equal_to(&view.leading)
.offset(10.),
image_view.top.constraint_equal_to(&view.top).offset(10.),
image_view
.bottom
.constraint_equal_to(&view.bottom)
.offset(-10.),
]);
Self {
view,
image_view,
_image: image,
}
}
pub fn view(&self) -> &View {
&self.view
}
pub fn image_view(&self) -> &ImageView {
&self.image_view
}
}

View file

@ -4,7 +4,7 @@ version = "0.5.1"
edition = "2021" edition = "2021"
[features] [features]
default = ["config", "librashader", "wgpu-renderer"] default = []
clocked-serial = [] clocked-serial = []
librashader = [ librashader = [
"dep:librashader", "dep:librashader",
@ -34,9 +34,9 @@ error-colour = []
[dependencies] [dependencies]
rand = "0.8.5" rand = "0.8.5"
async-ringbuf = "0.2.1" async-ringbuf = "0.1"
futures = "0.3.30" futures = "0.3"
itertools = "0.13.0" itertools = "0.12"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_with = "3.0" serde_with = "3.0"
bytemuck = "1.14" bytemuck = "1.14"
@ -51,16 +51,13 @@ librashader-common = { workspace = true, optional = true }
directories = { version = "5.0", optional = true } directories = { version = "5.0", optional = true }
ron = { version = "0.8", optional = true } ron = { version = "0.8", optional = true }
lazy_static = "1.4" lazy_static = "1.4"
wgpu = { version = "22.0.0", optional = true } wgpu = { version = "0.20", optional = true }
thiserror = { workspace = true } thiserror = { workspace = true }
log = { workspace = true } log = { workspace = true }
anyhow = "1.0.86" anyhow = "1.0.86"
[build-dependencies] [build-dependencies]
naga = { version = "22.0.0", optional = true, features = [ naga = { version = "0.19", optional = true, features = ["wgsl-in", "spv-out"] }
"wgsl-in",
"spv-out",
] }
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
ash-molten = { version = "0.19.0", optional = true } ash-molten = { version = "0.16.0", optional = true }

View file

@ -1,11 +1,8 @@
use async_ringbuf::{traits::Split, AsyncHeapCons, AsyncHeapProd, AsyncHeapRb};
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
pub use async_ringbuf::traits::consumer::AsyncConsumer;
pub use crate::error::RomHeaderError; pub use crate::error::RomHeaderError;
pub use crate::processor::memory::mmio::gpu::Colour; pub use crate::processor::memory::mmio::gpu::Colour;
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState}; pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
@ -16,6 +13,7 @@ pub use crate::processor::memory::rom::{
}; };
pub use crate::processor::memory::Rom; pub use crate::processor::memory::Rom;
pub use crate::{HEIGHT, WIDTH}; pub use crate::{HEIGHT, WIDTH};
use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb};
#[derive(Debug)] #[derive(Debug)]
pub enum EmulatorMessage<ColourFormat> pub enum EmulatorMessage<ColourFormat>
@ -100,7 +98,7 @@ pub struct ResolutionData {
pub struct AudioOutput { pub struct AudioOutput {
pub sample_rate: f32, pub sample_rate: f32,
pub send_rb: AsyncHeapProd<[f32; 2]>, pub send_rb: AsyncHeapProducer<[f32; 2]>,
pub downsample_type: DownsampleType, pub downsample_type: DownsampleType,
} }
@ -109,7 +107,7 @@ impl AudioOutput {
sample_rate: f32, sample_rate: f32,
buffers_per_frame: usize, buffers_per_frame: usize,
downsample_type: DownsampleType, downsample_type: DownsampleType,
) -> (Self, AsyncHeapCons<[f32; 2]>) { ) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
let rb_len = (sample_rate as usize / 60) / buffers_per_frame; let rb_len = (sample_rate as usize / 60) / buffers_per_frame;
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len); let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);

View file

@ -7,7 +7,6 @@ use crate::{
processor::memory::addresses::{AddressMarker, AudioAddress, WaveRamAddress}, processor::memory::addresses::{AddressMarker, AudioAddress, WaveRamAddress},
util::{get_bit, set_or_clear_bit}, util::{get_bit, set_or_clear_bit},
}; };
use async_ringbuf::traits::{AsyncProducer, Observer};
use futures::executor; use futures::executor;
use itertools::izip; use itertools::izip;

View file

@ -97,7 +97,6 @@ impl RendererBackend for WgpuBackend {
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER, required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_limits: wgpu::Limits::default(), required_limits: wgpu::Limits::default(),
label: None, label: None,
memory_hints: wgpu::MemoryHints::default(),
}, },
None, None,
) )

View file

@ -6,14 +6,14 @@ use gb_emu_lib::connect::{EmulatorCoreTrait, RomFile};
mod common; mod common;
#[test] #[test]
fn cpu_instrs() -> anyhow::Result<()> { fn cpu_instrs() -> Result<(), String> {
run_blargg_test("cpu_instrs\n\n01:ok 02:ok 03:ok 04:ok 05:ok 06:ok 07:ok 08:ok 09:ok 10:ok 11:ok \n\nPassed all tests", include_bytes!( run_blargg_test("cpu_instrs\n\n01:ok 02:ok 03:ok 04:ok 05:ok 06:ok 07:ok 08:ok 09:ok 10:ok 11:ok \n\nPassed all tests", include_bytes!(
"../../test-roms/blargg/cpu_instrs/cpu_instrs.gb" "../../test-roms/blargg/cpu_instrs/cpu_instrs.gb"
),None) ),None)
} }
#[test] #[test]
fn instr_timing() -> anyhow::Result<()> { fn instr_timing() -> Result<(), String> {
run_blargg_test( run_blargg_test(
"instr_timing\n\n\nPassed", "instr_timing\n\n\nPassed",
include_bytes!("../../test-roms/blargg/instr_timing/instr_timing.gb"), include_bytes!("../../test-roms/blargg/instr_timing/instr_timing.gb"),
@ -22,7 +22,7 @@ fn instr_timing() -> anyhow::Result<()> {
} }
#[test] #[test]
fn mem_timing() -> anyhow::Result<()> { fn mem_timing() -> Result<(), String> {
run_blargg_test( run_blargg_test(
"mem_timing\n\n\nPassed", "mem_timing\n\n\nPassed",
include_bytes!("../../test-roms/blargg/mem_timing/mem_timing.gb"), include_bytes!("../../test-roms/blargg/mem_timing/mem_timing.gb"),
@ -30,17 +30,11 @@ fn mem_timing() -> anyhow::Result<()> {
) )
} }
#[derive(Debug, thiserror::Error)]
enum TestError {
#[error("Timeout")]
Timeout(String),
}
fn run_blargg_test<const N: usize>( fn run_blargg_test<const N: usize>(
correct_output: &str, correct_output: &str,
rom: &[u8; N], rom: &[u8; N],
extra_end: Option<Vec<&str>>, extra_end: Option<Vec<&str>>,
) -> anyhow::Result<()> { ) -> Result<(), String> {
let mut emu = emulator_setup(RomFile::Raw(rom.to_vec()))?; let mut emu = emulator_setup(RomFile::Raw(rom.to_vec()))?;
let mut end_strings = extra_end.unwrap_or_default(); let mut end_strings = extra_end.unwrap_or_default();
@ -60,7 +54,7 @@ fn run_blargg_test<const N: usize>(
} }
} }
if began.elapsed() > timeout { if began.elapsed() > timeout {
return Err(TestError::Timeout(format!("Test timed out: output was {chars}")).into()); return Err(format!("Test timed out: output was {chars}"));
} }
} }

View file

@ -1,5 +1,6 @@
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use async_ringbuf::AsyncHeapConsumer;
use gb_emu_lib::{ use gb_emu_lib::{
connect::{AudioOutput, EmulatorMessage, EmulatorOptions, RomFile}, connect::{AudioOutput, EmulatorMessage, EmulatorOptions, RomFile},
EmulatorCore, EmulatorCore,
@ -7,14 +8,16 @@ use gb_emu_lib::{
pub struct TestEmulator { pub struct TestEmulator {
pub core: EmulatorCore<[u8; 4]>, pub core: EmulatorCore<[u8; 4]>,
pub _sender: Sender<EmulatorMessage<[u8; 4]>>, pub sender: Sender<EmulatorMessage<[u8; 4]>>,
pub _audio_rx: async_ringbuf::AsyncHeapCons<[f32; 2]>, pub audio_rx: AsyncHeapConsumer<[f32; 2]>,
pub serial_rx: Receiver<u8>, pub serial_rx: Receiver<u8>,
} }
pub fn emulator_setup(rom_file: RomFile) -> anyhow::Result<TestEmulator> { pub fn emulator_setup(rom_file: RomFile) -> Result<TestEmulator, String> {
let (sender, receiver) = channel::<EmulatorMessage<[u8; 4]>>(); let (sender, receiver) = channel::<EmulatorMessage<[u8; 4]>>();
let rom = rom_file.load(gb_emu_lib::connect::SramType::None)?; let rom = rom_file
.load(gb_emu_lib::connect::SramType::None)
.map_err(|_e| String::from("Error reading ROM: {_e:?}"))?;
let (audio_output, audio_rx) = AudioOutput::new( let (audio_output, audio_rx) = AudioOutput::new(
48000., 48000.,
1, 1,
@ -37,12 +40,14 @@ pub fn emulator_setup(rom_file: RomFile) -> anyhow::Result<TestEmulator> {
include_bytes!("../../../sameboy-bootroms/dmg_boot.bin").to_vec(), include_bytes!("../../../sameboy-bootroms/dmg_boot.bin").to_vec(),
))); )));
let core = EmulatorCore::init(true, receiver, options)?; let core = EmulatorCore::init(true, receiver, options);
sender.send(EmulatorMessage::Start)?; sender
.send(EmulatorMessage::Start)
.map_err(|_e| String::from("Error sending message: {_e:?}"))?;
Ok(TestEmulator { Ok(TestEmulator {
core, core,
_sender: sender, sender,
_audio_rx: audio_rx, audio_rx,
serial_rx, serial_rx,
}) })
} }