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]
|
||||
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> {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
73
src/ppu.rs
73
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::<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