pub use palette::{PaletteVram, PaletteVramMulti, PaletteVramSingle};
use sprite::SpriteVramInner;
pub use dynamic::{DynamicSprite16, DynamicSprite256};
pub use sprite::SpriteVram;
use crate::{display::palette16::Palette16, hash_map::HashMap, util::SyncUnsafeCell};
use super::sprite::{Palette, PaletteMulti, Sprite};
mod dynamic;
mod palette;
mod sprite;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct SpriteId(usize);
impl SpriteId {
fn new(sprite: &'static Sprite) -> Self {
Self(sprite as *const _ as usize)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct PaletteId(usize);
impl PaletteId {
fn new_single(palette: &'static Palette16) -> Self {
Self(palette as *const _ as usize)
}
fn new_multi(palette: &'static PaletteMulti) -> Self {
Self(palette as *const _ as usize)
}
fn new(palette: Palette) -> Self {
match palette {
Palette::Single(palette16) => Self::new_single(palette16),
Palette::Multi(palette_multi) => Self::new_multi(palette_multi),
}
}
}
struct SpriteLoaderInner {
palettes: HashMap<PaletteId, PaletteVram>,
sprites: HashMap<SpriteId, SpriteVramInner>,
}
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LoaderError {
SpriteFull,
PaletteFull,
}
impl SpriteLoaderInner {
pub(crate) const fn new() -> Self {
Self {
palettes: HashMap::new(),
sprites: HashMap::new(),
}
}
fn garbage_collect_sprites(&mut self) {
self.sprites.retain(|_, v| v.strong_count() > 1);
}
fn garbage_collect_palettes(&mut self) {
self.palettes.retain(|_, v| v.strong_count() > 1);
}
fn try_allocate_palette_inner(&mut self, palette: Palette) -> Result<PaletteVram, LoaderError> {
match self.palettes.entry(PaletteId::new(palette)) {
agb_hashmap::Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
agb_hashmap::Entry::Vacant(vacant_entry) => {
let palette = match palette {
Palette::Single(palette16) => PaletteVram::new_single(palette16),
Palette::Multi(palette_multi) => PaletteVram::new_multi(palette_multi),
}?;
vacant_entry.insert(palette.clone());
Ok(palette)
}
}
}
fn try_allocate_palette(&mut self, palette: Palette) -> Result<PaletteVram, LoaderError> {
if let Ok(palette) = self.try_allocate_palette_inner(palette) {
return Ok(palette);
}
self.garbage_collect_palettes();
self.try_allocate_palette_inner(palette)
}
fn try_allocate_sprite_inner(
&mut self,
sprite: &'static Sprite,
) -> Result<SpriteVramInner, LoaderError> {
match self.sprites.entry(SpriteId::new(sprite)) {
agb_hashmap::Entry::Occupied(occupied_entry) => {
let sprite = occupied_entry.get();
Ok(sprite.clone())
}
agb_hashmap::Entry::Vacant(vacant_entry) => {
let sprite = SpriteVramInner::new_from_sprite(sprite)?;
vacant_entry.insert(sprite.clone());
Ok(sprite)
}
}
}
fn try_allocate_sprite(&mut self, sprite: &'static Sprite) -> Result<SpriteVram, LoaderError> {
let palette = self.try_allocate_palette(sprite.palette)?;
let sprite = match self.try_allocate_sprite_inner(sprite) {
Ok(sprite) => sprite,
Err(_) => {
self.garbage_collect_sprites();
self.try_allocate_sprite_inner(sprite)?
}
};
Ok(SpriteVram::new(sprite, palette))
}
}
pub struct SpriteLoader(SyncUnsafeCell<SpriteLoaderInner>);
impl SpriteLoader {
unsafe fn with<F, U>(&self, f: F) -> U
where
F: FnOnce(&mut SpriteLoaderInner) -> U,
{
unsafe { f(&mut *self.0.get()) }
}
pub unsafe fn sprite(&self, sprite: &'static Sprite) -> Result<SpriteVram, LoaderError> {
unsafe { self.with(|x| x.try_allocate_sprite(sprite)) }
}
pub unsafe fn palette(&self, palette: Palette) -> Result<PaletteVram, LoaderError> {
unsafe { self.with(|x| x.try_allocate_palette(palette)) }
}
}
pub(crate) unsafe fn garbage_collect_sprite_loader() {
unsafe {
SPRITE_LOADER.with(|x| {
x.garbage_collect_sprites();
x.garbage_collect_palettes();
});
}
}
pub static SPRITE_LOADER: SpriteLoader =
SpriteLoader(SyncUnsafeCell::new(SpriteLoaderInner::new()));
impl From<&'static Palette16> for PaletteVram {
fn from(value: &'static Palette16) -> Self {
PaletteVram::new_single(value).expect("out of palette space")
}
}
impl From<&'static Palette16> for PaletteVramSingle {
fn from(value: &'static Palette16) -> Self {
PaletteVramSingle::new(value)
}
}
impl TryFrom<PaletteVram> for PaletteVramSingle {
type Error = PaletteVram;
fn try_from(value: PaletteVram) -> Result<Self, Self::Error> {
value.single()
}
}
impl From<&'static PaletteMulti> for PaletteVramMulti {
fn from(value: &'static PaletteMulti) -> Self {
PaletteVramMulti::new(value)
}
}
impl TryFrom<PaletteVram> for PaletteVramMulti {
type Error = PaletteVram;
fn try_from(value: PaletteVram) -> Result<Self, Self::Error> {
value.multi()
}
}
impl From<PaletteVramSingle> for PaletteVram {
fn from(value: PaletteVramSingle) -> Self {
value.palette()
}
}
impl From<PaletteVramMulti> for PaletteVram {
fn from(value: PaletteVramMulti) -> Self {
value.palette()
}
}
impl From<&'static Sprite> for SpriteVram {
fn from(value: &'static Sprite) -> Self {
unsafe { SPRITE_LOADER.sprite(value) }.expect("have space for sprites")
}
}