diff --git a/agb/examples/animated_background.rs b/agb/examples/animated_background.rs index d0dab120..29c14469 100644 --- a/agb/examples/animated_background.rs +++ b/agb/examples/animated_background.rs @@ -17,7 +17,6 @@ fn main(mut gba: agb::Gba) -> ! { let vblank = agb::interrupt::VBlank::get(); 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); @@ -28,7 +27,7 @@ fn main(mut gba: agb::Gba) -> ! { bg.set_tile( &mut vram, (x, y).into(), - tileset_ref, + &tileset, TileSetting::new(0, false, false, 0), ); } @@ -41,7 +40,7 @@ fn main(mut gba: agb::Gba) -> ! { loop { i = (i + 1) % 8; - vram.replace_tile(tileset_ref, 0, tileset_ref, i); + vram.replace_tile(&tileset, 0, &tileset, i); vblank.wait_for_vblank(); } diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index ef0278d0..9620b36b 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -53,7 +53,6 @@ fn main(mut gba: agb::Gba) -> ! { vram.set_background_palette_raw(&MAP_PALETTE); let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp); - let tileset_ref = vram.add_tileset(tileset); let mut background = gfx.background(agb::display::Priority::P0); @@ -62,7 +61,7 @@ fn main(mut gba: agb::Gba) -> ! { background.set_tile( &mut vram, (i % 32, i / 32).into(), - tileset_ref, + &tileset, TileSetting::from_raw(tile), ); } diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 46db1afc..f53b35f1 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -6,7 +6,6 @@ pub fn display_logo(map: &mut RegularMap, vram: &mut VRamManager) { vram.set_background_palettes(agb_logo::test_logo.palettes); 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 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 tile_setting = TileSetting::new(tile_id, false, false, palette_entry); - map.set_tile( - vram, - (x, y).into(), - background_tilemap_reference, - tile_setting, - ); + map.set_tile(vram, (x, y).into(), &background_tilemap, tile_setting); } } diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index dba4bef2..e50d184e 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -1,6 +1,6 @@ use alloc::boxed::Box; -use super::{MapLoan, RegularMap, TileSetReference, TileSetting, VRamManager}; +use super::{MapLoan, RegularMap, TileSet, TileSetting, VRamManager}; use crate::{ display, @@ -9,7 +9,7 @@ use crate::{ pub struct InfiniteScrolledMap<'a> { map: MapLoan<'a, RegularMap>, - tile: Box) -> (TileSetReference, TileSetting)>, + tile: Box) -> (&'a TileSet<'a>, TileSetting) + 'a>, current_pos: Vector2D, offset: Vector2D, @@ -26,7 +26,7 @@ pub enum PartialUpdateStatus { impl<'a> InfiniteScrolledMap<'a> { pub fn new( map: MapLoan<'a, RegularMap>, - tile: Box) -> (TileSetReference, TileSetting)>, + tile: Box) -> (&'a TileSet<'a>, TileSetting) + 'a>, ) -> Self { Self { map, @@ -79,12 +79,12 @@ impl<'a> InfiniteScrolledMap<'a> { { for (x_idx, x) in (x_start..x_end).enumerate() { 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( vram, (x_idx as u16, (y_idx + copy_from as usize) as u16).into(), - tile_set_ref, + tileset, tile_setting, ); } @@ -172,7 +172,7 @@ impl<'a> InfiniteScrolledMap<'a> { .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( vram, @@ -181,7 +181,7 @@ impl<'a> InfiniteScrolledMap<'a> { (tile_y - self.offset.y).rem_euclid(32) as u16, ) .into(), - tile_set_ref, + tileset, tile_setting, ); } diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index ca33ee63..4cfdfd43 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -7,7 +7,7 @@ use crate::dma::dma_copy16; use crate::fixnum::Vector2D; use crate::memory_mapped::MemoryMapped; -use super::{Tile, TileSetReference, TileSetting, VRamManager}; +use super::{Tile, TileSet, TileSetting, VRamManager}; pub struct RegularMap { background_id: u8, @@ -42,7 +42,7 @@ impl RegularMap { &mut self, vram: &mut VRamManager, pos: Vector2D, - tileset_ref: TileSetReference, + tileset: &TileSet<'_>, tile_setting: TileSetting, ) { let pos = (pos.x + pos.y * 32) as usize; @@ -55,7 +55,7 @@ impl RegularMap { let tile_index = tile_setting.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) } else { Tile::default() diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 0e2e042a..b1d239f8 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -6,7 +6,7 @@ mod vram_manager; pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus}; pub use map::{MapLoan, RegularMap}; 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)] #[repr(transparent)] diff --git a/agb/src/display/tiled/vram_manager.rs b/agb/src/display/tiled/vram_manager.rs index 3b4aa57a..3fa5689d 100644 --- a/agb/src/display/tiled/vram_manager.rs +++ b/agb/src/display/tiled/vram_manager.rs @@ -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) }; -#[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)] pub enum TileFormat { FourBpp, @@ -59,17 +47,9 @@ impl<'a> TileSet<'a> { pub fn new(tiles: &'a [u8], format: TileFormat) -> Self { Self { tiles, format } } -} -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct TileSetReference { - id: u16, - generation: u16, -} - -impl TileSetReference { - fn new(id: u16, generation: u16) -> Self { - Self { id, generation } + fn reference(&self) -> NonNull<[u8]> { + self.tiles.into() } } @@ -89,81 +69,68 @@ impl TileIndex { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct TileReference(NonNull); -enum ArenaStorageItem { - EndOfFreeList, - NextFree(usize), - Data(T, u16), +#[derive(Clone, PartialEq, Eq, Hash)] +struct TileInTileSetReference { + tileset: NonNull<[u8]>, + tile: u16, } -pub struct VRamManager<'a> { - tilesets: Vec>>, - generation: u16, - free_pointer: Option, - - tile_set_to_vram: HashMap<(u16, u16), TileReference>, - reference_counts: Vec<(u16, Option<(TileSetReference, u16)>)>, -} - -impl<'a> VRamManager<'a> { - pub(crate) fn new() -> Self { - let tile_set_to_vram: HashMap<(u16, u16), TileReference> = HashMap::with_capacity(256); - +impl TileInTileSetReference { + fn new(tileset: &'_ TileSet<'_>, tile: u16) -> Self { Self { - tilesets: Vec::new(), - generation: 0, - free_pointer: None, + tileset: tileset.reference(), + tile, + } + } +} - tile_set_to_vram, - reference_counts: Default::default(), +#[derive(Clone, Default)] +struct TileReferenceCount { + reference_count: u16, + tile_in_tile_set: Option, +} + +impl TileReferenceCount { + fn new(tile_in_tile_set: TileInTileSetReference) -> Self { + Self { + reference_count: 1, + tile_in_tile_set: Some(tile_in_tile_set), } } - 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) + fn increment_reference_count(&mut self) { + self.reference_count += 1; } - pub fn remove_tileset(&mut self, tile_set_ref: TileSetReference) { - let tileset = &self.tilesets[tile_set_ref.id as usize]; + fn decrement_reference_count(&mut self) -> u16 { + assert!( + self.reference_count > 0, + "Trying to decrease the reference count below 0", + ); - match tileset { - ArenaStorageItem::Data(_, generation) => { - debug_assert_eq!( - *generation, tile_set_ref.generation, - "Tileset generation must be the same when removing" - ); + self.reference_count -= 1; + self.reference_count + } - self.tilesets[tile_set_ref.id as usize] = if let Some(ptr) = self.free_pointer { - ArenaStorageItem::NextFree(ptr) - } else { - ArenaStorageItem::EndOfFreeList - }; + fn clear(&mut self) { + self.reference_count = 0; + self.tile_in_tile_set = None; + } +} - self.free_pointer = Some(tile_set_ref.id as usize); - } - _ => panic!("Must remove valid tileset"), +pub struct VRamManager { + tile_set_to_vram: HashMap, + reference_counts: Vec, +} + +impl VRamManager { + pub(crate) fn new() -> Self { + let tile_set_to_vram: HashMap = + HashMap::with_capacity(256); + + Self { + tile_set_to_vram, + reference_counts: Default::default(), } } @@ -177,12 +144,14 @@ impl<'a> VRamManager<'a> { TileReference(NonNull::new(ptr as *mut _).unwrap()) } - pub(crate) fn add_tile(&mut self, tile_set_ref: TileSetReference, tile: u16) -> TileIndex { - let reference = self.tile_set_to_vram.get(&(tile_set_ref.id, tile)); + pub(crate) fn add_tile(&mut self, tile_set: &TileSet<'_>, tile: u16) -> TileIndex { + let reference = self + .tile_set_to_vram + .get(&TileInTileSetReference::new(tile_set, tile)); if let Some(reference) = reference { let index = Self::index_from_reference(*reference); - self.reference_counts[index].0 += 1; + self.reference_counts[index].increment_reference_count(); return TileIndex::new(index); } @@ -190,32 +159,30 @@ impl<'a> VRamManager<'a> { unsafe { TILE_ALLOCATOR.alloc(TILE_LAYOUT) }.unwrap().cast(); 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); self.tile_set_to_vram - .insert((tile_set_ref.id, tile), tile_reference); + .insert(TileInTileSetReference::new(tile_set, tile), tile_reference); - self.reference_counts - .resize(self.reference_counts.len().max(index + 1), (0, None)); + self.reference_counts.resize( + self.reference_counts.len().max(index + 1), + Default::default(), + ); - self.reference_counts[index] = (1, Some((tile_set_ref, tile))); + self.reference_counts[index] = + TileReferenceCount::new(TileInTileSetReference::new(tile_set, tile)); TileIndex::new(index) } pub(crate) fn remove_tile(&mut self, tile_index: TileIndex) { let index = tile_index.index() as usize; - assert!( - self.reference_counts[index].0 > 0, - "Trying to decrease the reference count of {} below 0", - index - ); - self.reference_counts[index].0 -= 1; + let new_reference_count = self.reference_counts[index].decrement_reference_count(); - if self.reference_counts[index].0 != 0 { + if new_reference_count != 0 { return; } @@ -224,45 +191,39 @@ impl<'a> VRamManager<'a> { TILE_ALLOCATOR.dealloc_no_normalise(tile_reference.0.cast().as_ptr(), TILE_LAYOUT); } - let tile_ref = self.reference_counts[index].1.unwrap(); - self.tile_set_to_vram.remove(&(tile_ref.0.id, tile_ref.1)); - self.reference_counts[index].1 = None; + let tile_ref = self.reference_counts[index] + .tile_in_tile_set + .as_ref() + .unwrap(); + + self.tile_set_to_vram.remove(tile_ref); + self.reference_counts[index].clear(); } pub fn replace_tile( &mut self, - source_tile_set_ref: TileSetReference, + source_tile_set: &TileSet<'_>, source_tile: u16, - target_tile_set_ref: TileSetReference, + target_tile_set: &TileSet<'_>, target_tile: u16, ) { if let Some(&reference) = self .tile_set_to_vram - .get(&(source_tile_set_ref.id, source_tile)) + .get(&TileInTileSetReference::new(source_tile_set, 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( &self, - tile_set_ref: TileSetReference, + tile_set: &TileSet<'_>, tile_id: u16, tile_reference: TileReference, ) { - let tile_slice = if let ArenaStorageItem::Data(data, generation) = - &self.tilesets[tile_set_ref.id as usize] - { - 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 = tile_set.format.tile_size(); + let tile_offset = (tile_id as usize) * tile_size; + let tile_slice = &tile_set.tiles[tile_offset..(tile_offset + tile_size)]; let tile_size_in_half_words = tile_slice.len() / 2; diff --git a/agb/src/display/video.rs b/agb/src/display/video.rs index 046ff12c..57ae60e3 100644 --- a/agb/src/display/video.rs +++ b/agb/src/display/video.rs @@ -18,7 +18,7 @@ impl Video { unsafe { Bitmap4::new() } } - pub fn tiled0(&mut self) -> (Tiled0, VRamManager<'_>) { + pub fn tiled0(&mut self) -> (Tiled0, VRamManager) { (unsafe { Tiled0::new() }, VRamManager::new()) } } diff --git a/examples/the-hat-chooses-the-wizard/src/level_display.rs b/examples/the-hat-chooses-the-wizard/src/level_display.rs index 867a5244..4019ad55 100644 --- a/examples/the-hat-chooses-the-wizard/src/level_display.rs +++ b/examples/the-hat-chooses-the-wizard/src/level_display.rs @@ -1,5 +1,5 @@ use agb::display::{ - tiled::{RegularMap, TileSetReference, TileSetting, VRamManager}, + tiled::{RegularMap, TileSet, TileSetting, VRamManager}, HEIGHT, WIDTH, }; @@ -12,7 +12,7 @@ pub fn write_level( map: &mut RegularMap, world: u32, level: u32, - tile_set_ref: TileSetReference, + tileset: &'_ TileSet<'_>, vram: &mut VRamManager, ) { for (i, &tile) in [ @@ -30,7 +30,7 @@ pub fn write_level( map.set_tile( vram, (i as u16, 0).into(), - tile_set_ref, + &tileset, TileSetting::from_raw(tile), ); } diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 1589e1b4..65c63571 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -783,17 +783,14 @@ fn main(mut agb: agb::Gba) -> ! { let mut splash_screen = tiled.background(Priority::P0); let mut world_display = tiled.background(Priority::P0); - let tile_set_ref = vram.add_tileset(TileSet::new( - tile_sheet::background.tiles, - TileFormat::FourBpp, - )); + let tileset = TileSet::new(tile_sheet::background.tiles, TileFormat::FourBpp); for y in 0..32u16 { for x in 0..32u16 { world_display.set_tile( &mut vram, (x, y).into(), - tile_set_ref, + &tileset, TileSetting::from_raw(level_display::BLANK), ); } @@ -837,7 +834,7 @@ fn main(mut agb: agb::Gba) -> ! { &mut world_display, current_level / 8 + 1, current_level % 8 + 1, - tile_set_ref, + &tileset, &mut vram, ); @@ -849,12 +846,13 @@ fn main(mut agb: agb::Gba) -> ! { vblank.wait_for_vblank(); mixer.after_vblank(); + let map_current_level = current_level; let mut background = InfiniteScrolledMap::new( tiled.background(Priority::P2), - Box::new(move |pos: Vector2D| { - let level = &map_tiles::LEVELS[current_level as usize]; + Box::new(|pos: Vector2D| { + let level = &map_tiles::LEVELS[map_current_level as usize]; ( - tile_set_ref, + &tileset, TileSetting::from_raw( *level .background @@ -866,10 +864,10 @@ fn main(mut agb: agb::Gba) -> ! { ); let mut foreground = InfiniteScrolledMap::new( tiled.background(Priority::P0), - Box::new(move |pos: Vector2D| { - let level = &map_tiles::LEVELS[current_level as usize]; + Box::new(|pos: Vector2D| { + let level = &map_tiles::LEVELS[map_current_level as usize]; ( - tile_set_ref, + &tileset, TileSetting::from_raw( *level .foreground diff --git a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs index 4605d74d..b39e39f9 100644 --- a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs +++ b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs @@ -19,22 +19,19 @@ pub fn show_splash_screen( vram: &mut VRamManager, ) { map.set_scroll_pos((0u16, 0u16).into()); - let (tile_set_ref, palette) = match which { + let (tileset, palette) = match which { SplashScreen::Start => { - let tile_set_ref = vram.add_tileset(TileSet::new( - splash_screens::splash.tiles, - TileFormat::FourBpp, - )); + let tileset = TileSet::new(splash_screens::splash.tiles, TileFormat::FourBpp); - (tile_set_ref, splash_screens::splash.palettes) + (tileset, splash_screens::splash.palettes) } SplashScreen::End => { - let tile_set_ref = vram.add_tileset(TileSet::new( + let tileset = TileSet::new( splash_screens::thanks_for_playing.tiles, TileFormat::FourBpp, - )); + ); - (tile_set_ref, splash_screens::thanks_for_playing.palettes) + (tileset, splash_screens::thanks_for_playing.palettes) } }; @@ -60,7 +57,7 @@ pub fn show_splash_screen( map.set_tile( vram, (x, y).into(), - tile_set_ref, + &tileset, TileSetting::from_raw(y * 30 + x), ); } @@ -108,6 +105,4 @@ pub fn show_splash_screen( map.hide(); map.clear(vram); - - vram.remove_tileset(tile_set_ref); } diff --git a/examples/the-purple-night/src/main.rs b/examples/the-purple-night/src/main.rs index 6ab5aa03..b881e24b 100644 --- a/examples/the-purple-night/src/main.rs +++ b/examples/the-purple-night/src/main.rs @@ -2223,18 +2223,15 @@ fn game_with_level(gba: &mut agb::Gba) { vram.set_background_palettes(background::background.palettes); - let tileset_ref = vram.add_tileset(TileSet::new( - background::background.tiles, - TileFormat::FourBpp, - )); + let tileset = TileSet::new(background::background.tiles, TileFormat::FourBpp); let object = gba.display.object.get(); let backdrop = InfiniteScrolledMap::new( background.background(Priority::P2), - Box::new(move |pos| { + Box::new(|pos| { ( - tileset_ref, + &tileset, TileSetting::from_raw( *tilemap::BACKGROUND_MAP .get((pos.x + tilemap::WIDTH * pos.y) as usize) @@ -2246,9 +2243,9 @@ fn game_with_level(gba: &mut agb::Gba) { let foreground = InfiniteScrolledMap::new( background.background(Priority::P0), - Box::new(move |pos| { + Box::new(|pos| { ( - tileset_ref, + &tileset, TileSetting::from_raw( *tilemap::FOREGROUND_MAP .get((pos.x + tilemap::WIDTH * pos.y) as usize) @@ -2260,9 +2257,9 @@ fn game_with_level(gba: &mut agb::Gba) { let clouds = InfiniteScrolledMap::new( background.background(Priority::P3), - Box::new(move |pos| { + Box::new(|pos| { ( - tileset_ref, + &tileset, TileSetting::from_raw( *tilemap::CLOUD_MAP .get((pos.x + tilemap::WIDTH * pos.y) as usize)