From d6bfde081ed4919a52edfd4265eb20d7f147d0be Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sat, 5 Nov 2022 02:02:41 -0300 Subject: [PATCH] feat(ppu): implement double buffering --- src/emu.rs | 3 ++- src/gui.rs | 2 +- src/ppu.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/emu.rs b/src/emu.rs index 96943c5..ed150b8 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -44,7 +44,8 @@ pub fn save_and_exit(cpu: &Cpu, control_flow: &mut ControlFlow) { #[inline] pub fn pixel_buf(cpu: &Cpu) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] { - cpu.bus.ppu.frame_buf.as_ref() + use crate::ppu::Device; + cpu.bus.ppu.frame_buf.get(Device::Host) } pub fn from_boot_rom>(path: P) -> std::io::Result { diff --git a/src/gui.rs b/src/gui.rs index f07c12a..a24b8e0 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -363,7 +363,7 @@ fn surface_config( format, width: size.width as u32, height: size.height as u32, - present_mode: PresentMode::Immediate, + present_mode: PresentMode::Mailbox, alpha_mode, } } diff --git a/src/ppu.rs b/src/ppu.rs index 62b4fb4..689e3d7 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -58,7 +58,7 @@ pub struct Ppu { fetch: PixelFetcher, fifo: PixelFifo, obj_buffer: ObjectBuffer, - pub(crate) frame_buf: Box<[u8; (GB_WIDTH * 4) * GB_HEIGHT]>, + pub(crate) frame_buf: FrameBuffer, win_stat: WindowStatus, scanline_start: bool, @@ -83,7 +83,9 @@ impl Ppu { if !self.ctrl.lcd_enabled() { if self.dot > 0 { // Check ensures this expensive operation only happens once - self.frame_buf.copy_from_slice(BLANK_SCREEN.as_ref()); + self.frame_buf + .get_mut(Device::Guest) + .copy_from_slice(BLANK_SCREEN.as_ref()); } self.stat.set_mode(PpuMode::HBlank); @@ -178,6 +180,9 @@ impl Ppu { self.int.set_lcd_stat(true); } + // Screen is done drawing + self.frame_buf.swap(); + PpuMode::VBlank } else { if self.stat.oam_int() { @@ -411,8 +416,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; } @@ -474,7 +479,7 @@ impl Default for Ppu { Self { vram: Box::new([0u8; VRAM_SIZE]), dot: Default::default(), - frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]), + frame_buf: FrameBuffer::new().expect("create frame buffers"), int: Default::default(), ctrl: LCDControl(0), monochrome: Default::default(), @@ -949,3 +954,63 @@ 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::() * GB_HEIGHT; + + pub fn new() -> Result { + 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, +}