feat(ppu): implement double buffering

This commit is contained in:
Rekai Nyangadzayi Musuka 2022-11-05 02:02:41 -03:00
parent d2f57ee66b
commit d6bfde081e
3 changed files with 72 additions and 6 deletions

View File

@ -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<P: AsRef<Path>>(path: P) -> std::io::Result<Cpu> {

View File

@ -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,
}
}

View File

@ -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::<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,
}