Remove tile set ref in agb

This commit is contained in:
Gwilym Kuiper 2022-03-28 20:53:05 +01:00
parent c43581620f
commit 16ea04d012
8 changed files with 38 additions and 138 deletions

View file

@ -17,7 +17,6 @@ fn main(mut gba: agb::Gba) -> ! {
let vblank = agb::interrupt::VBlank::get(); let vblank = agb::interrupt::VBlank::get();
let tileset = TileSet::new(water_tiles::water_tiles.tiles, TileFormat::FourBpp); let tileset = TileSet::new(water_tiles::water_tiles.tiles, TileFormat::FourBpp);
let tileset_ref = vram.add_tileset(tileset);
vram.set_background_palettes(water_tiles::water_tiles.palettes); vram.set_background_palettes(water_tiles::water_tiles.palettes);
@ -28,7 +27,7 @@ fn main(mut gba: agb::Gba) -> ! {
bg.set_tile( bg.set_tile(
&mut vram, &mut vram,
(x, y).into(), (x, y).into(),
tileset_ref, &tileset,
TileSetting::new(0, false, false, 0), TileSetting::new(0, false, false, 0),
); );
} }
@ -41,7 +40,7 @@ fn main(mut gba: agb::Gba) -> ! {
loop { loop {
i = (i + 1) % 8; i = (i + 1) % 8;
vram.replace_tile(tileset_ref, 0, tileset_ref, i); vram.replace_tile(&tileset, 0, &tileset, i);
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }

View file

@ -53,7 +53,6 @@ fn main(mut gba: agb::Gba) -> ! {
vram.set_background_palette_raw(&MAP_PALETTE); vram.set_background_palette_raw(&MAP_PALETTE);
let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp); let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp);
let tileset_ref = vram.add_tileset(tileset);
let mut background = gfx.background(agb::display::Priority::P0); let mut background = gfx.background(agb::display::Priority::P0);
@ -62,7 +61,7 @@ fn main(mut gba: agb::Gba) -> ! {
background.set_tile( background.set_tile(
&mut vram, &mut vram,
(i % 32, i / 32).into(), (i % 32, i / 32).into(),
tileset_ref, &tileset,
TileSetting::from_raw(tile), TileSetting::from_raw(tile),
); );
} }

View file

@ -6,7 +6,6 @@ pub fn display_logo(map: &mut RegularMap, vram: &mut VRamManager) {
vram.set_background_palettes(agb_logo::test_logo.palettes); vram.set_background_palettes(agb_logo::test_logo.palettes);
let background_tilemap = TileSet::new(agb_logo::test_logo.tiles, TileFormat::FourBpp); let background_tilemap = TileSet::new(agb_logo::test_logo.tiles, TileFormat::FourBpp);
let background_tilemap_reference = vram.add_tileset(background_tilemap);
for y in 0..20 { for y in 0..20 {
for x in 0..30 { for x in 0..30 {
@ -15,12 +14,7 @@ pub fn display_logo(map: &mut RegularMap, vram: &mut VRamManager) {
let palette_entry = agb_logo::test_logo.palette_assignments[tile_id as usize]; let palette_entry = agb_logo::test_logo.palette_assignments[tile_id as usize];
let tile_setting = TileSetting::new(tile_id, false, false, palette_entry); let tile_setting = TileSetting::new(tile_id, false, false, palette_entry);
map.set_tile( map.set_tile(vram, (x, y).into(), &background_tilemap, tile_setting);
vram,
(x, y).into(),
background_tilemap_reference,
tile_setting,
);
} }
} }

View file

@ -1,6 +1,6 @@
use alloc::boxed::Box; use alloc::boxed::Box;
use super::{MapLoan, RegularMap, TileSetReference, TileSetting, VRamManager}; use super::{MapLoan, RegularMap, TileSet, TileSetting, VRamManager};
use crate::{ use crate::{
display, display,
@ -9,7 +9,7 @@ use crate::{
pub struct InfiniteScrolledMap<'a> { pub struct InfiniteScrolledMap<'a> {
map: MapLoan<'a, RegularMap>, map: MapLoan<'a, RegularMap>,
tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>, tile: Box<dyn Fn(Vector2D<i32>) -> (&'a TileSet<'a>, TileSetting)>,
current_pos: Vector2D<i32>, current_pos: Vector2D<i32>,
offset: Vector2D<i32>, offset: Vector2D<i32>,
@ -26,7 +26,7 @@ pub enum PartialUpdateStatus {
impl<'a> InfiniteScrolledMap<'a> { impl<'a> InfiniteScrolledMap<'a> {
pub fn new( pub fn new(
map: MapLoan<'a, RegularMap>, map: MapLoan<'a, RegularMap>,
tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>, tile: Box<dyn Fn(Vector2D<i32>) -> (&'a TileSet<'a>, TileSetting)>,
) -> Self { ) -> Self {
Self { Self {
map, map,
@ -79,12 +79,12 @@ impl<'a> InfiniteScrolledMap<'a> {
{ {
for (x_idx, x) in (x_start..x_end).enumerate() { for (x_idx, x) in (x_start..x_end).enumerate() {
let pos = (x, y).into(); let pos = (x, y).into();
let (tile_set_ref, tile_setting) = (self.tile)(pos); let (tileset, tile_setting) = (self.tile)(pos);
self.map.set_tile( self.map.set_tile(
vram, vram,
(x_idx as u16, (y_idx + copy_from as usize) as u16).into(), (x_idx as u16, (y_idx + copy_from as usize) as u16).into(),
tile_set_ref, tileset,
tile_setting, tile_setting,
); );
} }
@ -172,7 +172,7 @@ impl<'a> InfiniteScrolledMap<'a> {
.iter() .iter()
.chain(horizontal_rect_to_update.iter()) .chain(horizontal_rect_to_update.iter())
{ {
let (tile_set_ref, tile_setting) = (self.tile)((tile_x, tile_y).into()); let (tileset, tile_setting) = (self.tile)((tile_x, tile_y).into());
self.map.set_tile( self.map.set_tile(
vram, vram,
@ -181,7 +181,7 @@ impl<'a> InfiniteScrolledMap<'a> {
(tile_y - self.offset.y).rem_euclid(32) as u16, (tile_y - self.offset.y).rem_euclid(32) as u16,
) )
.into(), .into(),
tile_set_ref, tileset,
tile_setting, tile_setting,
); );
} }

View file

@ -7,7 +7,7 @@ use crate::dma::dma_copy16;
use crate::fixnum::Vector2D; use crate::fixnum::Vector2D;
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
use super::{Tile, TileSetReference, TileSetting, VRamManager}; use super::{Tile, TileSet, TileSetting, VRamManager};
pub struct RegularMap { pub struct RegularMap {
background_id: u8, background_id: u8,
@ -42,7 +42,7 @@ impl RegularMap {
&mut self, &mut self,
vram: &mut VRamManager, vram: &mut VRamManager,
pos: Vector2D<u16>, pos: Vector2D<u16>,
tileset_ref: TileSetReference, tileset: &TileSet<'_>,
tile_setting: TileSetting, tile_setting: TileSetting,
) { ) {
let pos = (pos.x + pos.y * 32) as usize; let pos = (pos.x + pos.y * 32) as usize;
@ -55,7 +55,7 @@ impl RegularMap {
let tile_index = tile_setting.index(); let tile_index = tile_setting.index();
let new_tile = if tile_index != TRANSPARENT_TILE_INDEX { let new_tile = if tile_index != TRANSPARENT_TILE_INDEX {
let new_tile_idx = vram.add_tile(tileset_ref, tile_index); let new_tile_idx = vram.add_tile(tileset, tile_index);
Tile::new(new_tile_idx, tile_setting) Tile::new(new_tile_idx, tile_setting)
} else { } else {
Tile::default() Tile::default()

View file

@ -6,7 +6,7 @@ mod vram_manager;
pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus}; pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus};
pub use map::{MapLoan, RegularMap}; pub use map::{MapLoan, RegularMap};
pub use tiled0::Tiled0; pub use tiled0::Tiled0;
pub use vram_manager::{TileFormat, TileIndex, TileSet, TileSetReference, VRamManager}; pub use vram_manager::{TileFormat, TileIndex, TileSet, VRamManager};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)] #[repr(transparent)]

View file

@ -24,18 +24,6 @@ static TILE_ALLOCATOR: BlockAllocator = unsafe {
const TILE_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(8 * 8 / 2, 8 * 8 / 2) }; const TILE_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(8 * 8 / 2, 8 * 8 / 2) };
#[cfg(debug_assertions)]
unsafe fn debug_unreachable_unchecked(message: &'static str) -> ! {
unreachable!("{}", message);
}
#[cfg(not(debug_assertions))]
const unsafe fn debug_unreachable_unchecked(_message: &'static str) -> ! {
use core::hint::unreachable_unchecked;
unreachable_unchecked();
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum TileFormat { pub enum TileFormat {
FourBpp, FourBpp,
@ -59,17 +47,9 @@ impl<'a> TileSet<'a> {
pub fn new(tiles: &'a [u8], format: TileFormat) -> Self { pub fn new(tiles: &'a [u8], format: TileFormat) -> Self {
Self { tiles, format } Self { tiles, format }
} }
}
#[derive(Clone, Copy, PartialEq, Eq)] fn reference(&self) -> NonNull<[u8]> {
pub struct TileSetReference { self.tiles.into()
id: u16,
generation: u16,
}
impl TileSetReference {
fn new(id: u16, generation: u16) -> Self {
Self { id, generation }
} }
} }
@ -89,84 +69,22 @@ impl TileIndex {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct TileReference(NonNull<u32>); struct TileReference(NonNull<u32>);
enum ArenaStorageItem<T> { pub struct VRamManager {
EndOfFreeList, tile_set_to_vram: HashMap<(NonNull<[u8]>, u16), TileReference>,
NextFree(usize), reference_counts: Vec<(u16, Option<(NonNull<[u8]>, u16)>)>,
Data(T, u16),
} }
pub struct VRamManager<'a> { impl VRamManager {
tilesets: Vec<ArenaStorageItem<TileSet<'a>>>,
generation: u16,
free_pointer: Option<usize>,
tile_set_to_vram: HashMap<(u16, u16), TileReference>,
reference_counts: Vec<(u16, Option<(TileSetReference, u16)>)>,
}
impl<'a> VRamManager<'a> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let tile_set_to_vram: HashMap<(u16, u16), TileReference> = HashMap::with_capacity(256); let tile_set_to_vram: HashMap<(NonNull<[u8]>, u16), TileReference> =
HashMap::with_capacity(256);
Self { Self {
tilesets: Vec::new(),
generation: 0,
free_pointer: None,
tile_set_to_vram, tile_set_to_vram,
reference_counts: Default::default(), reference_counts: Default::default(),
} }
} }
pub fn add_tileset(&mut self, tileset: TileSet<'a>) -> TileSetReference {
let generation = self.generation;
self.generation = self.generation.wrapping_add(1);
let tileset = ArenaStorageItem::Data(tileset, generation);
let index = if let Some(ptr) = self.free_pointer.take() {
match self.tilesets[ptr] {
ArenaStorageItem::EndOfFreeList => {
self.tilesets[ptr] = tileset;
ptr
}
ArenaStorageItem::NextFree(next_free) => {
self.free_pointer = Some(next_free);
self.tilesets[ptr] = tileset;
ptr
}
_ => unsafe { debug_unreachable_unchecked("Free pointer cannot point to data") },
}
} else {
self.tilesets.push(tileset);
self.tilesets.len() - 1
};
TileSetReference::new(index as u16, generation)
}
pub fn remove_tileset(&mut self, tile_set_ref: TileSetReference) {
let tileset = &self.tilesets[tile_set_ref.id as usize];
match tileset {
ArenaStorageItem::Data(_, generation) => {
debug_assert_eq!(
*generation, tile_set_ref.generation,
"Tileset generation must be the same when removing"
);
self.tilesets[tile_set_ref.id as usize] = if let Some(ptr) = self.free_pointer {
ArenaStorageItem::NextFree(ptr)
} else {
ArenaStorageItem::EndOfFreeList
};
self.free_pointer = Some(tile_set_ref.id as usize);
}
_ => panic!("Must remove valid tileset"),
}
}
fn index_from_reference(reference: TileReference) -> usize { fn index_from_reference(reference: TileReference) -> usize {
let difference = reference.0.as_ptr() as usize - TILE_RAM_START; let difference = reference.0.as_ptr() as usize - TILE_RAM_START;
difference / (8 * 8 / 2) difference / (8 * 8 / 2)
@ -177,8 +95,8 @@ impl<'a> VRamManager<'a> {
TileReference(NonNull::new(ptr as *mut _).unwrap()) TileReference(NonNull::new(ptr as *mut _).unwrap())
} }
pub(crate) fn add_tile(&mut self, tile_set_ref: TileSetReference, tile: u16) -> TileIndex { pub(crate) fn add_tile(&mut self, tile_set: &TileSet<'_>, tile: u16) -> TileIndex {
let reference = self.tile_set_to_vram.get(&(tile_set_ref.id, tile)); let reference = self.tile_set_to_vram.get(&(tile_set.reference(), tile));
if let Some(reference) = reference { if let Some(reference) = reference {
let index = Self::index_from_reference(*reference); let index = Self::index_from_reference(*reference);
@ -190,17 +108,17 @@ impl<'a> VRamManager<'a> {
unsafe { TILE_ALLOCATOR.alloc(TILE_LAYOUT) }.unwrap().cast(); unsafe { TILE_ALLOCATOR.alloc(TILE_LAYOUT) }.unwrap().cast();
let tile_reference = TileReference(new_reference); let tile_reference = TileReference(new_reference);
self.copy_tile_to_location(tile_set_ref, tile, tile_reference); self.copy_tile_to_location(tile_set, tile, tile_reference);
let index = Self::index_from_reference(tile_reference); let index = Self::index_from_reference(tile_reference);
self.tile_set_to_vram self.tile_set_to_vram
.insert((tile_set_ref.id, tile), tile_reference); .insert((tile_set.reference(), tile), tile_reference);
self.reference_counts self.reference_counts
.resize(self.reference_counts.len().max(index + 1), (0, None)); .resize(self.reference_counts.len().max(index + 1), (0, None));
self.reference_counts[index] = (1, Some((tile_set_ref, tile))); self.reference_counts[index] = (1, Some((tile_set.reference(), tile)));
TileIndex::new(index) TileIndex::new(index)
} }
@ -225,44 +143,34 @@ impl<'a> VRamManager<'a> {
} }
let tile_ref = self.reference_counts[index].1.unwrap(); let tile_ref = self.reference_counts[index].1.unwrap();
self.tile_set_to_vram.remove(&(tile_ref.0.id, tile_ref.1)); self.tile_set_to_vram.remove(&tile_ref);
self.reference_counts[index].1 = None; self.reference_counts[index].1 = None;
} }
pub fn replace_tile( pub fn replace_tile(
&mut self, &mut self,
source_tile_set_ref: TileSetReference, source_tile_set: &TileSet<'_>,
source_tile: u16, source_tile: u16,
target_tile_set_ref: TileSetReference, target_tile_set: &TileSet<'_>,
target_tile: u16, target_tile: u16,
) { ) {
if let Some(&reference) = self if let Some(&reference) = self
.tile_set_to_vram .tile_set_to_vram
.get(&(source_tile_set_ref.id, source_tile)) .get(&(source_tile_set.reference(), source_tile))
{ {
self.copy_tile_to_location(target_tile_set_ref, target_tile, reference); self.copy_tile_to_location(target_tile_set, target_tile, reference);
} }
} }
fn copy_tile_to_location( fn copy_tile_to_location(
&self, &self,
tile_set_ref: TileSetReference, tile_set: &TileSet<'_>,
tile_id: u16, tile_id: u16,
tile_reference: TileReference, tile_reference: TileReference,
) { ) {
let tile_slice = if let ArenaStorageItem::Data(data, generation) = let tile_size = tile_set.format.tile_size();
&self.tilesets[tile_set_ref.id as usize] let tile_offset = (tile_id as usize) * tile_size;
{ let tile_slice = &tile_set.tiles[tile_offset..(tile_offset + tile_size)];
debug_assert_eq!(
*generation, tile_set_ref.generation,
"Stale tile data requested"
);
let tile_offset = (tile_id as usize) * data.format.tile_size();
&data.tiles[tile_offset..(tile_offset + data.format.tile_size())]
} else {
panic!("Target tile set ref must point to an existing tile set reference")
};
let tile_size_in_half_words = tile_slice.len() / 2; let tile_size_in_half_words = tile_slice.len() / 2;

View file

@ -18,7 +18,7 @@ impl Video {
unsafe { Bitmap4::new() } unsafe { Bitmap4::new() }
} }
pub fn tiled0(&mut self) -> (Tiled0, VRamManager<'_>) { pub fn tiled0(&mut self) -> (Tiled0, VRamManager) {
(unsafe { Tiled0::new() }, VRamManager::new()) (unsafe { Tiled0::new() }, VRamManager::new())
} }
} }