Compare commits

..

1 Commits

Author SHA1 Message Date
84ddf323ec chore: actually paint the gui 2022-10-21 01:16:36 -03:00
7 changed files with 905 additions and 1433 deletions

2150
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,16 +8,17 @@ edition = "2021"
[dependencies]
anyhow = "1.0"
bitfield = "0.17"
clap = { version = "4.0", features = ["cargo"] }
gilrs = "0.11"
winit = "0.28"
egui = "0.21"
egui_wgpu_backend = "0.22"
egui_winit_platform = "0.18"
pollster = "0.3"
rodio = "0.19"
rtrb = "0.3"
bitfield = "0.14"
clap = { version = "3.1", features = ["cargo"] }
gilrs = "0.9"
winit = "0.27"
egui = "0.19"
wgpu = "0.14"
egui_wgpu_backend = "0.20"
egui_winit_platform = "0.16"
pollster = "0.2"
rodio = "0.16"
rtrb = "0.2"
directories-next = "2.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }

View File

@@ -1,9 +1,10 @@
# Rekai's Gameboy Emulator
[![Build Status](https://ci.paoda.moe/api/badges/paoda/gb/status.svg)](https://ci.paoda.moe/paoda/gb)
### Status
* From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/)
* [x] cpu_instrs
* [x] instr_timing
* [ ] instr_timing (kind of)
* [x] mem_timing
* [x] mem_timing-2
* [ ] dmg_sound (partial)
@@ -20,15 +21,9 @@
Supports: ROM-only, MBC1, MBC2, MBC3 and MBC5 games.
### Compiling
This project was last successfully built on [Rust 1.64.0](https://github.com/rust-lang/rust/tree/1.64.0)
1. `git clone https://github.com/paoda/gb`
2. `cd gb`
3. `cargo run --release`
### Controls
Controls are defined [here](https://github.com/paoda/gb/blob/85940c874460b9cb31bf9b8211bf7dda596d114a/src/joypad.rs#L114)
Controls are defined [here](https://git.musuka.dev/paoda/gb/src/branch/main/src/joypad.rs#L114)
Key | Button
--- | ---

View File

@@ -44,8 +44,7 @@ pub fn save_and_exit(cpu: &Cpu, control_flow: &mut ControlFlow) {
#[inline]
pub fn pixel_buf(cpu: &Cpu) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
use crate::ppu::Device;
cpu.bus.ppu.frame_buf.get(Device::Host)
cpu.bus.ppu.frame_buf.as_ref()
}
pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Cpu> {

View File

@@ -1,12 +1,11 @@
use egui::{Context, TextureId};
use egui_wgpu_backend::{wgpu, RenderPass, ScreenDescriptor};
use egui_wgpu_backend::{RenderPass, ScreenDescriptor};
use egui_winit_platform::Platform;
use wgpu::{
Adapter, CommandEncoder, Device, Extent3d, FilterMode, Instance, Queue, RequestDeviceError,
Surface, SurfaceCapabilities, SurfaceConfiguration, Texture, TextureView,
TextureViewDescriptor,
Adapter, Backends, Color, CommandEncoder, CompositeAlphaMode, Device, Extent3d, FilterMode,
Instance, Queue, RequestDeviceError, Surface, SurfaceConfiguration, Texture, TextureFormat,
TextureUsages, TextureView, TextureViewDescriptor,
};
use winit::dpi::PhysicalSize;
use winit::error::OsError;
use winit::event::{ElementState, Event, KeyboardInput};
@@ -43,26 +42,20 @@ pub struct Gui {
impl Gui {
pub fn new<T>(title: String, event_loop: &EventLoop<T>, cpu: &Cpu) -> Self {
use wgpu::InstanceDescriptor;
let window = build_window(event_loop).expect("build window");
let instance = Instance::new(InstanceDescriptor::default());
let surface = unsafe {
instance
.create_surface(&window)
.expect("create wgpu instance surface")
};
let instance = Instance::new(Backends::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let adapter = request_adapter(&instance, &surface).expect("request adaptor");
let (device, queue) = request_device(&adapter).expect("request device");
let texture_format = surface.get_supported_formats(&adapter)[0]; // First is preferred
let capabilities = surface.get_capabilities(&adapter);
let surface_config = surface_config(&window, capabilities);
let alpha_mode = surface.get_supported_alpha_modes(&adapter)[0];
let surface_config = surface_config(&window, alpha_mode, texture_format);
surface.configure(&device, &surface_config);
let platform = platform(&window);
let mut render_pass = RenderPass::new(&device, surface_config.format, 1);
let mut render_pass = RenderPass::new(&device, texture_format, 1);
let texture_size = texture_size();
let texture = create_texture(&device, texture_size);
@@ -119,7 +112,7 @@ impl Gui {
}
pub fn paint(&mut self, cpu: &Cpu) {
use wgpu::{Color, SurfaceError};
use wgpu::SurfaceError;
let data = emu::pixel_buf(cpu);
write_to_texture(&self.queue, &self.texture, data, self.texture_size);
@@ -357,18 +350,21 @@ fn request_device(adapter: &Adapter) -> Result<(Device, Queue), RequestDeviceErr
))
}
fn surface_config(window: &Window, capabilities: SurfaceCapabilities) -> SurfaceConfiguration {
use egui_wgpu_backend::wgpu::{PresentMode, TextureFormat, TextureUsages};
fn surface_config(
window: &Window,
alpha_mode: CompositeAlphaMode,
format: TextureFormat,
) -> SurfaceConfiguration {
use wgpu::PresentMode;
let size = window.inner_size();
SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT,
format: capabilities.formats[0],
format,
width: size.width as u32,
height: size.height as u32,
present_mode: PresentMode::Fifo,
alpha_mode: capabilities.alpha_modes[0],
view_formats: vec![TextureFormat::Rgba8UnormSrgb],
present_mode: PresentMode::Immediate,
alpha_mode,
}
}
@@ -395,17 +391,16 @@ fn texture_size() -> Extent3d {
}
fn create_texture(device: &Device, size: Extent3d) -> Texture {
use wgpu::{TextureDescriptor, TextureDimension, TextureFormat, TextureUsages};
use wgpu::{TextureDescriptor, TextureDimension};
device.create_texture(&TextureDescriptor {
size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8Unorm,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
label: Some("gb_pixel_buffer"),
view_formats: &[TextureFormat::Bgra8Unorm],
})
}

View File

@@ -1,7 +1,6 @@
use std::path::PathBuf;
use std::time::Instant;
use clap::{arg, command, value_parser};
use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, Command};
use gb::gui::{EmuMode, Gui};
use gb::{emu, gui};
use gilrs::Gilrs;
@@ -13,16 +12,26 @@ use winit::event_loop::{EventLoop, EventLoopBuilder};
const AUDIO_ENABLED: bool = true;
fn main() {
let m = command!()
let app = Command::new(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!());
let m = app
.arg(
arg!(-b --boot <FILE> "path to boot ROM")
.required(false)
.value_parser(value_parser!(PathBuf)),
Arg::new("rom")
.value_name("ROM_FILE")
.takes_value(true)
.index(1)
.help("Path to the Game ROM"),
)
.arg(
arg!([ROM_FILE] "path to game ROM")
.required(true)
.value_parser(value_parser!(PathBuf)),
Arg::new("boot")
.short('b')
.long("boot")
.value_name("FILE")
.takes_value(true)
.help("Path to Boot ROM"),
)
.get_matches();
@@ -36,7 +45,7 @@ fn main() {
.init();
// Init CPU
let mut cpu = match m.get_one::<PathBuf>("boot") {
let mut cpu = match m.value_of("boot") {
Some(path) => {
tracing::info!("User-provided boot ROM");
emu::from_boot_rom(path).expect("initialize emulator with custom boot rom")
@@ -48,7 +57,7 @@ fn main() {
};
// Load ROM if filepath was provided
if let Some(path) = m.get_one::<PathBuf>("ROM_FILE") {
if let Some(path) = m.value_of("rom") {
tracing::info!("User-provided cartridge ROM");
emu::read_game_rom(&mut cpu, path).expect("read game rom from path");
}

View File

@@ -58,7 +58,7 @@ pub struct Ppu {
fetch: PixelFetcher,
fifo: PixelFifo,
obj_buffer: ObjectBuffer,
pub(crate) frame_buf: FrameBuffer,
pub(crate) frame_buf: Box<[u8; (GB_WIDTH * 4) * GB_HEIGHT]>,
win_stat: WindowStatus,
scanline_start: bool,
@@ -83,9 +83,7 @@ impl Ppu {
if !self.ctrl.lcd_enabled() {
if self.dot > 0 {
// Check ensures this expensive operation only happens once
self.frame_buf
.get_mut(Device::Guest)
.copy_from_slice(BLANK_SCREEN.as_ref());
self.frame_buf.copy_from_slice(BLANK_SCREEN.as_ref());
}
self.stat.set_mode(PpuMode::HBlank);
@@ -180,9 +178,6 @@ impl Ppu {
self.int.set_lcd_stat(true);
}
// Screen is done drawing
self.frame_buf.swap();
PpuMode::VBlank
} else {
if self.stat.oam_int() {
@@ -416,8 +411,8 @@ impl Ppu {
let x = self.x_pos as usize;
let i = (GB_WIDTH * 4) * y + (x * 4);
self.frame_buf[i..(i + rgba.len())].copy_from_slice(&rgba);
self.frame_buf.get_mut(Device::Guest)[i..(i + rgba.len())].copy_from_slice(&rgba);
self.x_pos += 1;
}
@@ -479,7 +474,7 @@ impl Default for Ppu {
Self {
vram: Box::new([0u8; VRAM_SIZE]),
dot: Default::default(),
frame_buf: FrameBuffer::new().expect("create frame buffers"),
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
int: Default::default(),
ctrl: LCDControl(0),
monochrome: Default::default(),
@@ -954,63 +949,3 @@ pub(crate) mod dbg {
ppu.dot
}
}
#[derive(Debug)]
pub struct FrameBuffer {
buf: [Box<[u8; Self::FRAME_LEN]>; 2],
current: bool,
}
#[derive(PartialEq)]
pub enum Device {
Guest,
Host,
}
impl FrameBuffer {
const FRAME_LEN: usize = GB_WIDTH * std::mem::size_of::<u32>() * GB_HEIGHT;
pub fn new() -> Result<Self, FrameBufferError> {
Ok(Self {
buf: [
vec![0; Self::FRAME_LEN]
.into_boxed_slice()
.try_into()
.map_err(|_| FrameBufferError::TryFrom)?,
vec![0; Self::FRAME_LEN]
.into_boxed_slice()
.try_into()
.map_err(|_| FrameBufferError::TryFrom)?,
],
current: false,
})
}
pub fn swap(&mut self) {
self.current = !self.current;
}
pub fn get_mut(&mut self, device: Device) -> &mut [u8; Self::FRAME_LEN] {
let idx = match device {
Device::Guest => self.current,
Device::Host => !self.current,
};
&mut *self.buf[idx as usize]
}
pub fn get(&self, device: Device) -> &[u8; Self::FRAME_LEN] {
let idx = match device {
Device::Guest => self.current,
Device::Host => !self.current,
};
&*self.buf[idx as usize]
}
}
#[derive(Debug, thiserror::Error)]
pub enum FrameBufferError {
#[error("Failed to coerce boxed slice to boxed array")]
TryFrom,
}