From fd9a5b51ba44f188864abec92ec9be0d3560d49f Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 15 Jul 2021 22:29:51 -0500 Subject: [PATCH] chore(ppu): reimplement background/window/sprite priority --- src/ppu.rs | 123 +++++++++++++++++++++++++---------------------- src/ppu/types.rs | 20 +++++--- 2 files changed, 79 insertions(+), 64 deletions(-) diff --git a/src/ppu.rs b/src/ppu.rs index e1c374b..f00a391 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -8,7 +8,7 @@ use std::convert::TryInto; pub(crate) use types::PpuMode; use types::{ BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette, - ObjectPaletteId, ObjectSize, Pixels, RenderPriority, TileDataAddress, + ObjectPaletteKind, ObjectSize, Pixels, RenderPriority, TileDataAddress, }; mod dma; @@ -47,7 +47,7 @@ pub struct Ppu { pub(crate) dma: DirectMemoryAccess, scan_state: OamScanState, fetch: PixelFetcher, - fifo: FifoRenderer, + fifo: PixelFifo, obj_buffer: ObjectBuffer, frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>, window_stat: WindowStatus, @@ -282,10 +282,7 @@ impl Ppu { let tbpp = Pixels::from_bytes(high, low); - let palette = match attr.flags.palette() { - ObjectPaletteId::Zero => self.monochrome.obj_palette_0, - ObjectPaletteId::One => self.monochrome.obj_palette_1, - }; + let palette_kind = attr.flags.palette(); let end = Pixels::PIXEL_COUNT - self.fifo.obj.len(); let start = Pixels::PIXEL_COUNT - end; @@ -296,11 +293,11 @@ impl Ppu { let x = if x_flip { 7 - i } else { i }; let priority = attr.flags.priority(); - let shade = palette.shade(tbpp.shade_id(x)); + let shade_id = tbpp.shade_id(x); - let fifo_info = ObjectFifoInfo { - shade, - palette, + let fifo_info = ObjPixelProperty { + shade_id, + palette_kind, priority, }; @@ -368,9 +365,7 @@ impl Ppu { self.fetch.back.next(SendToFifoTwo); } SendToFifoTwo => { - let palette = &self.monochrome.bg_palette; - - if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo, palette) { + if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) { self.fetch.x_pos += 1; self.fetch.back.next(TileNumber); self.fetch.back.tile = Default::default(); @@ -380,8 +375,6 @@ impl Ppu { } if self.fifo.is_enabled() { - use RenderPriority::*; - if self.x_pos == 0 && !self.fifo.back.is_empty() { let to_discard = self.pos.scroll_x % 8; @@ -391,39 +384,12 @@ impl Ppu { } // Handle Background Pixel and Sprite FIFO - let bg_enabled = self.ctrl.bg_win_enabled(); - let obj_enabled = self.ctrl.obj_enabled(); - let i0_colour = self.monochrome.bg_palette.i0_colour(); - - // FIXME: Is this the correct behaviour - let rgba_opt = self.fifo.back.pop_front().map(|bg_info| { - let bg_shade = if bg_enabled { bg_info.shade } else { i0_colour }; - - match self.fifo.obj.pop_front() { - Some(obj_info) => { - if obj_enabled { - match (obj_info.shade, obj_info.priority) { - (Some(obj_shade), BackgroundAndWindow) => match bg_info.shade { - GrayShade::White => obj_shade.into_rgba(), - _ => bg_shade.into_rgba(), - }, - (Some(obj_shade), Object) => obj_shade.into_rgba(), - (None, _) => bg_shade.into_rgba(), - } - } else { - bg_shade.into_rgba() - } - } - None => bg_shade.into_rgba(), - } - }); - - if let Some(rgba) = rgba_opt.as_ref() { + if let Some(rgba) = self.clock_fifo().map(GrayShade::into_rgba) { let y = self.pos.line_y as usize; 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[i..(i + rgba.len())].copy_from_slice(&rgba); self.x_pos += 1; } @@ -442,6 +408,49 @@ impl Ppu { pub fn copy_to_gui(&self, frame: &mut [u8]) { frame.copy_from_slice(self.frame_buf.as_ref()); } + + fn clock_fifo(&mut self) -> Option { + use ObjectPaletteKind::*; + use RenderPriority::*; + + let obj_palette_0 = &self.monochrome.obj_palette_0; + let obj_palette_1 = &self.monochrome.obj_palette_1; + + match self.fifo.back.pop_front() { + Some(bg_pixel) => match self.fifo.obj.pop_front() { + Some(obj_pixel) if self.ctrl.obj_enabled() => match obj_pixel.priority { + Object | BackgroundAndWindow if obj_pixel.shade_id == 0 => { + Some(self.bg_pixel(bg_pixel.shade_id)) + } + BackgroundAndWindow if bg_pixel.shade_id != 0 => { + Some(self.bg_pixel(bg_pixel.shade_id)) + } + Object | BackgroundAndWindow => { + let maybe_sprite = match obj_pixel.palette_kind { + Zero => obj_palette_0.shade(obj_pixel.shade_id), + One => obj_palette_1.shade(obj_pixel.shade_id), + }; + + let sprite = maybe_sprite + .expect("Sprite w/ a colour id of 0 has already been handled"); + Some(sprite) + } + }, + _ => Some(self.bg_pixel(bg_pixel.shade_id)), + }, + None => None, + } + } + + fn bg_pixel(&self, shade_id: u8) -> GrayShade { + let bg_palette = &self.monochrome.bg_palette; + + if self.ctrl.bg_win_enabled() { + bg_palette.shade(shade_id) + } else { + bg_palette.shade(0) + } + } } impl Default for Ppu { @@ -721,7 +730,7 @@ impl PixelFetcher { tile_data_addr + (offset * 2) } - fn send_to_fifo(&self, fifo: &mut FifoRenderer, palette: &BackgroundPalette) -> Result<(), ()> { + fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> { if !fifo.back.is_empty() { return Err(()); } @@ -735,9 +744,9 @@ impl PixelFetcher { let tbpp = Pixels::from_bytes(high, low); for x in 0..Pixels::PIXEL_COUNT { - let shade = palette.shade(tbpp.shade_id(x)); + let shade_id = tbpp.shade_id(x); - let fifo_info = BackgroundFifoInfo { shade }; + let fifo_info = BgPixelProperty { shade_id }; fifo.back.push_back(fifo_info); } @@ -897,27 +906,27 @@ impl Default for FetcherState { } #[derive(Debug, Clone, Copy, Default)] -struct BackgroundFifoInfo { - shade: GrayShade, +struct BgPixelProperty { + shade_id: u8, } #[derive(Debug, Clone, Copy, Default)] -struct ObjectFifoInfo { - shade: Option, - palette: ObjectPalette, +struct ObjPixelProperty { + shade_id: u8, + palette_kind: ObjectPaletteKind, priority: RenderPriority, } // FIXME: Fifo Registers have a known size. Are heap allocations // really necessary here? #[derive(Debug, Clone)] -struct FifoRenderer { - back: VecDeque, - obj: VecDeque, +struct PixelFifo { + back: VecDeque, + obj: VecDeque, enabled: bool, } -impl FifoRenderer { +impl PixelFifo { fn is_enabled(&self) -> bool { self.enabled } @@ -931,7 +940,7 @@ impl FifoRenderer { } } -impl Default for FifoRenderer { +impl Default for PixelFifo { fn default() -> Self { Self { back: VecDeque::with_capacity(8), diff --git a/src/ppu/types.rs b/src/ppu/types.rs index 27ac28a..6b3ab44 100644 --- a/src/ppu/types.rs +++ b/src/ppu/types.rs @@ -332,7 +332,7 @@ bitfield! { pub from into RenderPriority, priority, set_priority: 7, 7; pub y_flip, set_y_flip: 6; pub x_flip, set_x_flip: 5; - pub from into ObjectPaletteId, palette, set_palette: 4, 4; + pub from into ObjectPaletteKind, palette, set_palette: 4, 4; } impl Eq for ObjectFlags {} @@ -368,23 +368,29 @@ impl Default for ObjectFlags { } #[derive(Debug, Clone, Copy)] -pub enum ObjectPaletteId { +pub enum ObjectPaletteKind { Zero = 0, One = 1, } -impl From for ObjectPaletteId { +impl Default for ObjectPaletteKind { + fn default() -> Self { + Self::Zero + } +} + +impl From for ObjectPaletteKind { fn from(byte: u8) -> Self { match byte & 0b01 { - 0b00 => ObjectPaletteId::Zero, - 0b01 => ObjectPaletteId::One, + 0b00 => ObjectPaletteKind::Zero, + 0b01 => ObjectPaletteKind::One, _ => unreachable!("{:#04X} is not a valid value for BgPaletteNumber", byte), } } } -impl From for u8 { - fn from(palette_num: ObjectPaletteId) -> Self { +impl From for u8 { + fn from(palette_num: ObjectPaletteKind) -> Self { palette_num as u8 } }