feat(ppu): implement double buffering
This commit is contained in:
parent
d2f57ee66b
commit
d6bfde081e
|
@ -44,7 +44,8 @@ pub fn save_and_exit(cpu: &Cpu, control_flow: &mut ControlFlow) {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pixel_buf(cpu: &Cpu) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
|
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> {
|
pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Cpu> {
|
||||||
|
|
|
@ -363,7 +363,7 @@ fn surface_config(
|
||||||
format,
|
format,
|
||||||
width: size.width as u32,
|
width: size.width as u32,
|
||||||
height: size.height as u32,
|
height: size.height as u32,
|
||||||
present_mode: PresentMode::Immediate,
|
present_mode: PresentMode::Mailbox,
|
||||||
alpha_mode,
|
alpha_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
src/ppu.rs
73
src/ppu.rs
|
@ -58,7 +58,7 @@ pub struct Ppu {
|
||||||
fetch: PixelFetcher,
|
fetch: PixelFetcher,
|
||||||
fifo: PixelFifo,
|
fifo: PixelFifo,
|
||||||
obj_buffer: ObjectBuffer,
|
obj_buffer: ObjectBuffer,
|
||||||
pub(crate) frame_buf: Box<[u8; (GB_WIDTH * 4) * GB_HEIGHT]>,
|
pub(crate) frame_buf: FrameBuffer,
|
||||||
win_stat: WindowStatus,
|
win_stat: WindowStatus,
|
||||||
|
|
||||||
scanline_start: bool,
|
scanline_start: bool,
|
||||||
|
@ -83,7 +83,9 @@ impl Ppu {
|
||||||
if !self.ctrl.lcd_enabled() {
|
if !self.ctrl.lcd_enabled() {
|
||||||
if self.dot > 0 {
|
if self.dot > 0 {
|
||||||
// Check ensures this expensive operation only happens once
|
// 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);
|
self.stat.set_mode(PpuMode::HBlank);
|
||||||
|
@ -178,6 +180,9 @@ impl Ppu {
|
||||||
self.int.set_lcd_stat(true);
|
self.int.set_lcd_stat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Screen is done drawing
|
||||||
|
self.frame_buf.swap();
|
||||||
|
|
||||||
PpuMode::VBlank
|
PpuMode::VBlank
|
||||||
} else {
|
} else {
|
||||||
if self.stat.oam_int() {
|
if self.stat.oam_int() {
|
||||||
|
@ -411,8 +416,8 @@ impl Ppu {
|
||||||
let x = self.x_pos as usize;
|
let x = self.x_pos as usize;
|
||||||
|
|
||||||
let i = (GB_WIDTH * 4) * y + (x * 4);
|
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;
|
self.x_pos += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +479,7 @@ impl Default for Ppu {
|
||||||
Self {
|
Self {
|
||||||
vram: Box::new([0u8; VRAM_SIZE]),
|
vram: Box::new([0u8; VRAM_SIZE]),
|
||||||
dot: Default::default(),
|
dot: Default::default(),
|
||||||
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
|
frame_buf: FrameBuffer::new().expect("create frame buffers"),
|
||||||
int: Default::default(),
|
int: Default::default(),
|
||||||
ctrl: LCDControl(0),
|
ctrl: LCDControl(0),
|
||||||
monochrome: Default::default(),
|
monochrome: Default::default(),
|
||||||
|
@ -949,3 +954,63 @@ pub(crate) mod dbg {
|
||||||
ppu.dot
|
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,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue