From 758f9849c3ddb90aaf0c0d45889af3c94af5c8f7 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 25 Sep 2022 13:30:25 +0100 Subject: [PATCH 01/11] track the lifetime of the static object controller --- agb/src/display/object.rs | 256 +++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 126 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 3888a8af..4ac981f5 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,10 +2,10 @@ use alloc::vec::Vec; use core::alloc::Layout; -use core::cell::UnsafeCell; +use core::cell::{Ref, RefCell, RefMut, UnsafeCell}; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::ops::{Deref, DerefMut}; +use core::ops::DerefMut; use core::ptr::NonNull; use core::slice; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; @@ -23,75 +23,46 @@ use crate::hash_map::HashMap; use attributes::*; -static mut OBJECT_CONTROLLER: MaybeUninit = MaybeUninit::uninit(); - -unsafe fn init_object_controller() { - OBJECT_CONTROLLER.write(ObjectControllerStatic::new()); -} - -unsafe fn uninit_object_controller() { - OBJECT_CONTROLLER.assume_init_drop(); -} - -struct ObjectControllerRef {} - -impl Deref for ObjectControllerRef { - type Target = ObjectControllerStatic; - fn deref(&self) -> &'static ObjectControllerStatic { - unsafe { OBJECT_CONTROLLER.assume_init_ref() } - } -} - -impl DerefMut for ObjectControllerRef { - fn deref_mut(&mut self) -> &'static mut ObjectControllerStatic { - unsafe { OBJECT_CONTROLLER.assume_init_mut() } - } -} - -#[cfg(debug_assertions)] -static OBJECT_REFS_CURRENT: bare_metal::Mutex> = - bare_metal::Mutex::new(core::cell::RefCell::new(0)); - -impl ObjectControllerRef { - fn new() -> Self { - #[cfg(debug_assertions)] - { - let a = crate::interrupt::free(|c| { - let mut b = OBJECT_REFS_CURRENT.borrow(c).borrow_mut(); - let a = *b; - *b += 1; - a - }); - assert_eq!(a, 0); - } - - Self {} - } - - unsafe fn very_unsafe_borrow(&self) -> &'static mut ObjectControllerStatic { - OBJECT_CONTROLLER.assume_init_mut() - } -} - -#[cfg(debug_assertions)] -impl Drop for ObjectControllerRef { - fn drop(&mut self) { - crate::interrupt::free(|c| { - let mut b = OBJECT_REFS_CURRENT.borrow(c).borrow_mut(); - *b -= 1; - }); - } -} - -unsafe fn get_object_controller(_r: ObjectControllerReference) -> ObjectControllerRef { - ObjectControllerRef::new() -} - /// Include this type if you call `get_object_controller` in impl block. This /// helps you use the right lifetimes and doesn't impl Sync (using from two /// "threads" without syncronisation is not safe), but sending to another /// "thread" is safe. -type ObjectControllerReference<'a> = PhantomData<&'a UnsafeCell<()>>; +#[derive(Clone, Copy)] +struct ObjectControllerReference<'a> { + #[cfg(debug_assertions)] + reference: &'a RefCell, + + _ref: PhantomData<&'a UnsafeCell<()>>, +} + +static mut OBJECT_CONTROLLER: MaybeUninit> = MaybeUninit::uninit(); + +impl<'a> ObjectControllerReference<'a> { + unsafe fn init() -> Self { + OBJECT_CONTROLLER.write(RefCell::new(ObjectControllerStatic::new())); + Self { + #[cfg(debug_assertions)] + reference: unsafe { OBJECT_CONTROLLER.assume_init_ref() }, + _ref: PhantomData, + } + } + + unsafe fn uninit() { + OBJECT_CONTROLLER.assume_init_drop(); + } + + #[track_caller] + fn borrow_mut(self) -> RefMut<'a, ObjectControllerStatic> { + #[cfg(debug_assertions)] + { + self.reference.borrow_mut() + } + #[cfg(not(debug_assertions))] + unsafe { + OBJECT_CONTROLLER.assume_init_ref().borrow_mut() + } + } +} static SPRITE_ALLOCATOR: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { @@ -458,7 +429,7 @@ pub struct SpriteBorrow<'a> { id: SpriteId, sprite_location: u16, palette_location: u16, - phantom: ObjectControllerReference<'a>, + controller: ObjectControllerReference<'a>, } #[derive(Clone, Copy)] @@ -545,12 +516,12 @@ struct SpriteControllerInner { struct Loan<'a> { index: u8, - phantom: ObjectControllerReference<'a>, + controller: ObjectControllerReference<'a>, } impl Drop for Loan<'_> { fn drop(&mut self) { - let mut s = unsafe { get_object_controller(self.phantom) }; + let mut s = self.controller.borrow_mut(); unsafe { s.shadow_oam[self.index as usize] @@ -598,13 +569,13 @@ impl ObjectControllerStatic { /// A controller that distributes objects and sprites. This controls sprites and /// objects being copied to vram when it needs to be. pub struct ObjectController { - phantom: ObjectControllerReference<'static>, + inner: ObjectControllerReference<'static>, } impl Drop for ObjectController { fn drop(&mut self) { unsafe { - uninit_object_controller(); + ObjectControllerReference::uninit(); } } } @@ -616,7 +587,7 @@ impl ObjectController { /// should be called shortly after having waited for the next vblank to /// ensure what is displayed on screen doesn't change part way through. pub fn commit(&self) { - let mut s = unsafe { get_object_controller(self.phantom) }; + let mut s = self.inner.borrow_mut(); let s = &mut *s; @@ -664,9 +635,8 @@ impl ObjectController { } } - unsafe { init_object_controller() }; Self { - phantom: PhantomData, + inner: unsafe { ObjectControllerReference::init() }, } } @@ -770,7 +740,7 @@ impl ObjectController { /// ``` #[must_use] pub fn try_get_object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Option> { - let mut s = unsafe { get_object_controller(self.phantom) }; + let mut s = self.inner.borrow_mut(); let mut attrs = Attributes::new(); @@ -795,7 +765,7 @@ impl ObjectController { let loan = Loan { index: index as u8, - phantom: PhantomData, + controller: self.inner, }; s.update_z_ordering(); @@ -850,28 +820,37 @@ impl ObjectController { /// ``` #[must_use] pub fn try_get_sprite(&self, sprite: &'static Sprite) -> Option { - let s = unsafe { get_object_controller(self.phantom) }; - unsafe { - s.very_unsafe_borrow() - .sprite_controller - .try_get_sprite(sprite) - } + let mut sprite_controller = + RefMut::map(self.inner.borrow_mut(), |c| &mut c.sprite_controller); + sprite_controller.try_get_sprite(sprite, self.inner) } } impl<'a> Object<'a> { #[inline(always)] - unsafe fn object_inner(&mut self) -> &mut ObjectInner { - let s = get_object_controller(self.loan.phantom); - s.very_unsafe_borrow().shadow_oam[self.loan.index as usize] - .as_mut() - .unwrap_unchecked() + unsafe fn object_inner(&self) -> RefMut { + RefMut::map(self.loan.controller.borrow_mut(), |s| { + s.shadow_oam[self.loan.index as usize] + .as_mut() + .unwrap_unchecked() + }) + } + + unsafe fn inner_controller(&self) -> (RefMut, RefMut) { + RefMut::map_split(self.loan.controller.borrow_mut(), |s| { + ( + &mut s.sprite_controller, + s.shadow_oam[self.loan.index as usize] + .as_mut() + .unwrap_unchecked(), + ) + }) } /// Swaps out the current sprite. This handles changing of size, palette, /// etc. No change will be seen until [ObjectController::commit] is called. pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) { - let object_inner = unsafe { self.object_inner() }; + let (mut sprite_controller, mut object_inner) = unsafe { self.inner_controller() }; object_inner.attrs.a2.set_tile_index(sprite.sprite_location); let shape_size = sprite.id.sprite().size.shape_size(); object_inner @@ -881,14 +860,19 @@ impl<'a> Object<'a> { object_inner.attrs.a0.set_shape(shape_size.0); object_inner.attrs.a1a.set_size(shape_size.1); object_inner.attrs.a1s.set_size(shape_size.1); - object_inner.sprite = unsafe { core::mem::transmute(sprite) }; + + let mut sprite = unsafe { core::mem::transmute(sprite) }; + core::mem::swap(&mut object_inner.sprite, &mut sprite); + sprite.drop(&mut sprite_controller); } /// Shows the sprite. No change will be seen until /// [ObjectController::commit] is called. pub fn show(&mut self) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_object_mode(ObjectMode::Normal); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a0.set_object_mode(ObjectMode::Normal); + } self } @@ -897,8 +881,10 @@ impl<'a> Object<'a> { /// for reusing the same sprite for the left and right walking directions. /// No change will be seen until [ObjectController::commit] is called. pub fn set_hflip(&mut self, flip: bool) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1s.set_horizontal_flip(flip); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a1s.set_horizontal_flip(flip); + } self } @@ -906,8 +892,10 @@ impl<'a> Object<'a> { /// for reusing the same sprite for the up and down walking directions. No /// change will be seen until [ObjectController::commit] is called. pub fn set_vflip(&mut self, flip: bool) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1s.set_vertical_flip(flip); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a1s.set_vertical_flip(flip); + } self } @@ -915,9 +903,11 @@ impl<'a> Object<'a> { /// corner of the sprite. No change will be seen until /// [ObjectController::commit] is called. pub fn set_x(&mut self, x: u16) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1a.set_x(x.rem_euclid(1 << 9) as u16); - object_inner.attrs.a1s.set_x(x.rem_euclid(1 << 9) as u16); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a1a.set_x(x.rem_euclid(1 << 9) as u16); + object_inner.attrs.a1s.set_x(x.rem_euclid(1 << 9) as u16); + } self } @@ -925,16 +915,20 @@ impl<'a> Object<'a> { /// above background layers with lower priorities. No change will be seen /// until [ObjectController::commit] is called. pub fn set_priority(&mut self, priority: Priority) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a2.set_priority(priority); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a2.set_priority(priority); + } self } /// Hides the object. No change will be seen until /// [ObjectController::commit] is called. pub fn hide(&mut self) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_object_mode(ObjectMode::Disabled); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a0.set_object_mode(ObjectMode::Disabled); + } self } @@ -942,8 +936,10 @@ impl<'a> Object<'a> { /// corner of the sprite. No change will be seen until /// [ObjectController::commit] is called. pub fn set_y(&mut self, y: u16) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_y(y as u8); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a0.set_y(y as u8); + } self } @@ -952,11 +948,11 @@ impl<'a> Object<'a> { /// eachother. No change will be seen until [ObjectController::commit] is /// called. pub fn set_z(&mut self, z: i32) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.z = z; - unsafe { - get_object_controller(self.loan.phantom).update_z_ordering(); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.z = z; } + self.loan.controller.borrow_mut().update_z_ordering(); self } @@ -965,16 +961,18 @@ impl<'a> Object<'a> { /// refers to the top-left corner of the sprite. No change will be seen /// until [ObjectController::commit] is called. pub fn set_position(&mut self, position: Vector2D) -> &mut Self { - let object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_y(position.y as u8); - object_inner - .attrs - .a1a - .set_x(position.x.rem_euclid(1 << 9) as u16); - object_inner - .attrs - .a1s - .set_x(position.x.rem_euclid(1 << 9) as u16); + { + let mut object_inner = unsafe { self.object_inner() }; + object_inner.attrs.a0.set_y(position.y as u8); + object_inner + .attrs + .a1a + .set_x(position.x.rem_euclid(1 << 9) as u16); + object_inner + .attrs + .a1s + .set_x(position.x.rem_euclid(1 << 9) as u16); + } self } } @@ -1033,7 +1031,11 @@ impl Sprite { } impl SpriteControllerInner { - fn try_get_sprite(&mut self, sprite: &'static Sprite) -> Option { + fn try_get_sprite<'a>( + &mut self, + sprite: &'static Sprite, + controller_reference: ObjectControllerReference<'a>, + ) -> Option> { let id = sprite.id(); if let Some(storage) = self.sprite.get_mut(&id) { storage.count += 1; @@ -1043,7 +1045,7 @@ impl SpriteControllerInner { id, palette_location, sprite_location: location, - phantom: PhantomData, + controller: controller_reference, }) } else { // layout is non zero sized, so this is safe to call @@ -1074,7 +1076,7 @@ impl SpriteControllerInner { id, palette_location, sprite_location: storage.location, - phantom: PhantomData, + controller: controller_reference, }) } } @@ -1141,7 +1143,7 @@ impl SpriteControllerInner { impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { - let mut s = unsafe { get_object_controller(self.phantom) }; + let mut s = self.controller.borrow_mut(); s.sprite_controller.return_sprite(self.id.sprite()); } } @@ -1159,14 +1161,14 @@ impl<'a> SpriteBorrow<'a> { id: self.id, sprite_location: self.sprite_location, palette_location: self.palette_location, - phantom: PhantomData, + controller: self.controller, } } } impl<'a> Clone for SpriteBorrow<'a> { fn clone(&self) -> Self { - let mut s = unsafe { get_object_controller(self.phantom) }; + let mut s = self.controller.borrow_mut(); self.clone(&mut s.sprite_controller) } } @@ -1243,7 +1245,9 @@ mod tests { #[test_case] fn size_of_ObjectControllerReference(_: &mut crate::Gba) { - assert_eq!(size_of::(), 0); + if !cfg!(debug_assertions) { + assert_eq!(size_of::(), 0); + } } #[test_case] From 781e1efc6f2743ebc295cab27e0dc24e97c05852 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 25 Sep 2022 13:38:50 +0100 Subject: [PATCH 02/11] satisfy linter --- agb/src/display/object.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 4ac981f5..0c2e218f 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,10 +2,9 @@ use alloc::vec::Vec; use core::alloc::Layout; -use core::cell::{Ref, RefCell, RefMut, UnsafeCell}; +use core::cell::{RefCell, RefMut, UnsafeCell}; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::ops::DerefMut; use core::ptr::NonNull; use core::slice; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; From 88e1635fb1ddcc6375666f2d6eabce3ddb07cb4f Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 25 Sep 2022 16:03:37 +0100 Subject: [PATCH 03/11] unify types --- agb/src/display/object.rs | 90 +++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 0c2e218f..d3d8b385 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,9 +2,10 @@ use alloc::vec::Vec; use core::alloc::Layout; -use core::cell::{RefCell, RefMut, UnsafeCell}; +use core::cell::UnsafeCell; use core::marker::PhantomData; use core::mem::MaybeUninit; +use core::ops::DerefMut; use core::ptr::NonNull; use core::slice; use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; @@ -29,16 +30,23 @@ use attributes::*; #[derive(Clone, Copy)] struct ObjectControllerReference<'a> { #[cfg(debug_assertions)] - reference: &'a RefCell, + reference: &'a core::cell::RefCell, _ref: PhantomData<&'a UnsafeCell<()>>, } -static mut OBJECT_CONTROLLER: MaybeUninit> = MaybeUninit::uninit(); +#[cfg(debug_assertions)] +static mut OBJECT_CONTROLLER: MaybeUninit> = + MaybeUninit::uninit(); +#[cfg(not(debug_assertions))] +static mut OBJECT_CONTROLLER: MaybeUninit = MaybeUninit::uninit(); impl<'a> ObjectControllerReference<'a> { unsafe fn init() -> Self { - OBJECT_CONTROLLER.write(RefCell::new(ObjectControllerStatic::new())); + #[cfg(debug_assertions)] + OBJECT_CONTROLLER.write(core::cell::RefCell::new(ObjectControllerStatic::new())); + #[cfg(not(debug_assertions))] + OBJECT_CONTROLLER.write(ObjectControllerStatic::new()); Self { #[cfg(debug_assertions)] reference: unsafe { OBJECT_CONTROLLER.assume_init_ref() }, @@ -51,14 +59,25 @@ impl<'a> ObjectControllerReference<'a> { } #[track_caller] - fn borrow_mut(self) -> RefMut<'a, ObjectControllerStatic> { + #[cfg(debug_assertions)] + fn borrow_cell_ref(self) -> core::cell::RefMut<'a, ObjectControllerStatic> { + self.reference.borrow_mut() + } + #[track_caller] + #[cfg(not(debug_assertions))] + unsafe fn borrow_direct(self) -> &'a mut ObjectControllerStatic { + unsafe { OBJECT_CONTROLLER.assume_init_mut() } + } + + #[track_caller] + unsafe fn borrow_mut(self) -> impl DerefMut + 'a { #[cfg(debug_assertions)] { self.reference.borrow_mut() } #[cfg(not(debug_assertions))] unsafe { - OBJECT_CONTROLLER.assume_init_ref().borrow_mut() + OBJECT_CONTROLLER.assume_init_mut() } } } @@ -520,7 +539,7 @@ struct Loan<'a> { impl Drop for Loan<'_> { fn drop(&mut self) { - let mut s = self.controller.borrow_mut(); + let mut s = unsafe { self.controller.borrow_mut() }; unsafe { s.shadow_oam[self.index as usize] @@ -586,7 +605,7 @@ impl ObjectController { /// should be called shortly after having waited for the next vblank to /// ensure what is displayed on screen doesn't change part way through. pub fn commit(&self) { - let mut s = self.inner.borrow_mut(); + let mut s = unsafe { self.inner.borrow_mut() }; let s = &mut *s; @@ -739,7 +758,7 @@ impl ObjectController { /// ``` #[must_use] pub fn try_get_object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Option> { - let mut s = self.inner.borrow_mut(); + let mut s = unsafe { self.inner.borrow_mut() }; let mut attrs = Attributes::new(); @@ -819,31 +838,58 @@ impl ObjectController { /// ``` #[must_use] pub fn try_get_sprite(&self, sprite: &'static Sprite) -> Option { - let mut sprite_controller = - RefMut::map(self.inner.borrow_mut(), |c| &mut c.sprite_controller); - sprite_controller.try_get_sprite(sprite, self.inner) + unsafe { self.inner.borrow_mut() } + .sprite_controller + .try_get_sprite(sprite, self.inner) } } impl<'a> Object<'a> { #[inline(always)] - unsafe fn object_inner(&self) -> RefMut { - RefMut::map(self.loan.controller.borrow_mut(), |s| { - s.shadow_oam[self.loan.index as usize] + unsafe fn object_inner(&self) -> impl DerefMut + 'a { + #[cfg(debug_assertions)] + { + core::cell::RefMut::map(self.loan.controller.borrow_cell_ref(), |s| { + s.shadow_oam[self.loan.index as usize] + .as_mut() + .unwrap_unchecked() + }) + } + #[cfg(not(debug_assertions))] + { + self.loan.controller.borrow_direct().shadow_oam[self.loan.index as usize] .as_mut() .unwrap_unchecked() - }) + } } - unsafe fn inner_controller(&self) -> (RefMut, RefMut) { - RefMut::map_split(self.loan.controller.borrow_mut(), |s| { + unsafe fn inner_controller( + &self, + ) -> ( + impl DerefMut + 'a, + impl DerefMut + 'a, + ) { + #[cfg(debug_assertions)] + { + core::cell::RefMut::map_split(self.loan.controller.borrow_cell_ref(), |s| { + ( + &mut s.sprite_controller, + s.shadow_oam[self.loan.index as usize] + .as_mut() + .unwrap_unchecked(), + ) + }) + } + #[cfg(not(debug_assertions))] + { + let s = self.loan.controller.borrow_direct(); ( &mut s.sprite_controller, s.shadow_oam[self.loan.index as usize] .as_mut() .unwrap_unchecked(), ) - }) + } } /// Swaps out the current sprite. This handles changing of size, palette, @@ -951,7 +997,7 @@ impl<'a> Object<'a> { let mut object_inner = unsafe { self.object_inner() }; object_inner.z = z; } - self.loan.controller.borrow_mut().update_z_ordering(); + unsafe { self.loan.controller.borrow_mut().update_z_ordering() }; self } @@ -1142,7 +1188,7 @@ impl SpriteControllerInner { impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { - let mut s = self.controller.borrow_mut(); + let mut s = unsafe { self.controller.borrow_mut() }; s.sprite_controller.return_sprite(self.id.sprite()); } } @@ -1167,7 +1213,7 @@ impl<'a> SpriteBorrow<'a> { impl<'a> Clone for SpriteBorrow<'a> { fn clone(&self) -> Self { - let mut s = self.controller.borrow_mut(); + let mut s = unsafe { self.controller.borrow_mut() }; self.clone(&mut s.sprite_controller) } } From 5d21f720a5640d521736092d93abad31a7f40098 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 14:52:35 +0100 Subject: [PATCH 04/11] add arena library --- agb/src/arena.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++ agb/src/lib.rs | 1 + 2 files changed, 101 insertions(+) create mode 100644 agb/src/arena.rs diff --git a/agb/src/arena.rs b/agb/src/arena.rs new file mode 100644 index 00000000..034ac3ff --- /dev/null +++ b/agb/src/arena.rs @@ -0,0 +1,100 @@ +use alloc::vec::Vec; + +pub struct Arena { + first: Option, + data: Vec>, +} +enum Item { + Next { + next: Option, + generation: usize, + }, + Item { + item: T, + generation: usize, + }, +} + +#[derive(Debug, Clone, Copy)] +pub struct Index { + generation: usize, + index: usize, +} + +impl Arena { + #[must_use] + pub fn new() -> Self { + Arena { + first: None, + data: Vec::new(), + } + } + pub fn remove(&mut self, idx: Index) { + if let Item::Item { + item: _, + generation, + } = self.data[idx.index] + { + if generation != idx.generation { + return; + } + + self.data[idx.index] = Item::Next { + next: self.first, + generation, + }; + self.first = Some(idx.index); + } + } + pub fn get_mut(&mut self, idx: Index) -> Option<&mut T> { + match &mut self.data[idx.index] { + Item::Next { + next: _, + generation: _, + } => None, + Item::Item { item, generation } => { + if *generation == idx.generation { + Some(item) + } else { + None + } + } + } + } + pub fn insert(&mut self, data: T) -> Index { + match self.first { + Some(idx) => { + let (next, generation) = match &self.data[idx] { + Item::Next { next, generation } => (*next, *generation), + _ => unreachable!(), + }; + self.data[idx] = Item::Item { + item: data, + generation: generation + 1, + }; + + self.first = next; + Index { + generation: generation + 1, + index: idx, + } + } + None => { + self.data.push(Item::Item { + item: data, + generation: 0, + }); + Index { + generation: 0, + index: self.data.len() - 1, + } + } + } + } +} + +impl Default for Arena { + fn default() -> Self { + Self::new() + } +} diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 764cdbfa..e80f0c70 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -164,6 +164,7 @@ mod memory_mapped; pub mod mgba; #[doc(inline)] pub use agb_fixnum as fixnum; +pub mod arena; /// Contains an implementation of a hashmap which suits the gameboy advance's hardware. pub mod hash_map; /// Simple random number generator From 1acf7142daab5d14ae0c4f564f3d84e03f53d385 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 17:14:41 +0100 Subject: [PATCH 05/11] switch to using rust Rc --- agb/src/display/object.rs | 360 ++++++++++++++------------------------ 1 file changed, 136 insertions(+), 224 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index d3d8b385..a2b9c250 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,4 +1,5 @@ #![deny(missing_docs)] +use alloc::rc::{Rc, Weak}; use alloc::vec::Vec; use core::alloc::Layout; @@ -398,6 +399,10 @@ impl Size { (self as u8 >> 2, self as u8 & 0b11) } + fn layout(self) -> Layout { + Layout::from_size_align(self.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() + } + #[must_use] /// Creates a size from width and height in pixels, panics if the width and /// height is not representable by GBA sprites. @@ -443,38 +448,9 @@ impl Size { /// counted and can be cloned to keep it in vram or otherwise manipulated. If /// objects no longer refer to this sprite, then it's vram slot is freed for the /// next sprite. This is obtained from the [ObjectController]. -pub struct SpriteBorrow<'a> { - id: SpriteId, - sprite_location: u16, - palette_location: u16, - controller: ObjectControllerReference<'a>, -} - -#[derive(Clone, Copy)] -struct Storage { - location: u16, - count: u16, -} - -impl Storage { - fn from_sprite_ptr(d: NonNull) -> Self { - Self { - location: (((d.as_ptr() as usize) - TILE_SPRITE) / BYTES_PER_TILE_4BPP) as u16, - count: 1, - } - } - fn from_palette_ptr(d: NonNull) -> Self { - Self { - location: ((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) as u16, - count: 1, - } - } - fn as_palette_ptr(self) -> *mut u8 { - (self.location as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 - } - fn as_sprite_ptr(self) -> *mut u8 { - (self.location as usize * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 - } +#[derive(Clone)] +pub struct SpriteBorrow { + sprite: SpriteVram, } #[derive(PartialEq, Eq)] @@ -527,9 +503,56 @@ pub struct Object<'a> { loan: Loan<'a>, } +#[derive(Clone, Copy)] +struct Location(usize); + +impl Location { + fn from_sprite_ptr(d: NonNull) -> Self { + Self(((d.as_ptr() as usize) - TILE_SPRITE) / BYTES_PER_TILE_4BPP) + } + fn from_palette_ptr(d: NonNull) -> Self { + Self((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) + } + fn as_palette_ptr(self) -> *mut u8 { + (self.0 as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 + } + fn as_sprite_ptr(self) -> *mut u8 { + (self.0 as usize * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 + } +} + +#[derive(Clone)] +struct PaletteVram(Rc); + +#[derive(Clone)] +struct SpriteVram(Rc); + +struct PaletteData { + location: Location, +} + +impl Drop for PaletteData { + fn drop(&mut self) { + unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) }; + } +} + +struct SpriteArena { + location: Location, + size: Size, + palette: PaletteVram, +} + +impl Drop for SpriteArena { + fn drop(&mut self) { + unsafe { SPRITE_ALLOCATOR.dealloc(self.location.as_sprite_ptr(), self.size.layout()) } + } +} + +#[derive(Default)] struct SpriteControllerInner { - palette: HashMap, - sprite: HashMap, + static_palette_map: HashMap>, + static_sprite_map: HashMap>, } struct Loan<'a> { @@ -552,8 +575,8 @@ impl Drop for Loan<'_> { struct ObjectInner { attrs: Attributes, - sprite: SpriteBorrow<'static>, - previous_sprite: SpriteBorrow<'static>, + sprite: SpriteBorrow, + previous_sprite: SpriteBorrow, destroy: bool, z: i32, } @@ -620,15 +643,10 @@ impl ObjectController { .write_volatile(HIDDEN_VALUE); } - let a = unsafe { s.shadow_oam[z as usize].take().unwrap_unchecked() }; - a.previous_sprite.drop(&mut s.sprite_controller); - a.sprite.drop(&mut s.sprite_controller); + let _ = unsafe { s.shadow_oam[z as usize].take().unwrap_unchecked() }; } else { o.attrs.commit(i); - - let mut a = o.sprite.clone(&mut s.sprite_controller); - core::mem::swap(&mut o.previous_sprite, &mut a); - a.drop(&mut s.sprite_controller); + o.previous_sprite = o.sprite.clone(); } } else { unsafe { @@ -731,7 +749,7 @@ impl ObjectController { /// # } /// ``` #[must_use] - pub fn object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Object<'a> { + pub fn object(&self, sprite: SpriteBorrow) -> Object { self.try_get_object(sprite).expect("No object available") } @@ -757,28 +775,28 @@ impl ObjectController { /// # } /// ``` #[must_use] - pub fn try_get_object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Option> { + pub fn try_get_object(&self, sprite: SpriteBorrow) -> Option { let mut s = unsafe { self.inner.borrow_mut() }; let mut attrs = Attributes::new(); - attrs.a2.set_tile_index(sprite.sprite_location); - let shape_size = sprite.id.sprite().size.shape_size(); - attrs.a2.set_palete_bank(sprite.palette_location as u8); + attrs.a2.set_tile_index(sprite.sprite.0.location.0 as u16); + let shape_size = sprite.sprite.0.size.shape_size(); + attrs + .a2 + .set_palete_bank((sprite.sprite.0.palette.0.location.0) as u8); attrs.a0.set_shape(shape_size.0); attrs.a1a.set_size(shape_size.1); attrs.a1s.set_size(shape_size.1); let index = s.free_object.pop()?; - let new_sprite: SpriteBorrow<'static> = unsafe { core::mem::transmute(sprite) }; - s.shadow_oam[index as usize] = Some(ObjectInner { attrs, z: 0, - previous_sprite: new_sprite.clone(&mut s.sprite_controller), + previous_sprite: sprite.clone(), destroy: false, - sprite: new_sprite, + sprite, }); let loan = Loan { @@ -840,7 +858,7 @@ impl ObjectController { pub fn try_get_sprite(&self, sprite: &'static Sprite) -> Option { unsafe { self.inner.borrow_mut() } .sprite_controller - .try_get_sprite(sprite, self.inner) + .try_get_sprite(sprite) } } @@ -863,52 +881,24 @@ impl<'a> Object<'a> { } } - unsafe fn inner_controller( - &self, - ) -> ( - impl DerefMut + 'a, - impl DerefMut + 'a, - ) { - #[cfg(debug_assertions)] - { - core::cell::RefMut::map_split(self.loan.controller.borrow_cell_ref(), |s| { - ( - &mut s.sprite_controller, - s.shadow_oam[self.loan.index as usize] - .as_mut() - .unwrap_unchecked(), - ) - }) - } - #[cfg(not(debug_assertions))] - { - let s = self.loan.controller.borrow_direct(); - ( - &mut s.sprite_controller, - s.shadow_oam[self.loan.index as usize] - .as_mut() - .unwrap_unchecked(), - ) - } - } - /// Swaps out the current sprite. This handles changing of size, palette, /// etc. No change will be seen until [ObjectController::commit] is called. - pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) { - let (mut sprite_controller, mut object_inner) = unsafe { self.inner_controller() }; - object_inner.attrs.a2.set_tile_index(sprite.sprite_location); - let shape_size = sprite.id.sprite().size.shape_size(); + pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow) { + let mut object_inner = unsafe { self.object_inner() }; object_inner .attrs .a2 - .set_palete_bank(sprite.palette_location as u8); + .set_tile_index(sprite.sprite.0.location.0 as u16); + let shape_size = sprite.sprite.0.size.shape_size(); + object_inner + .attrs + .a2 + .set_palete_bank(sprite.sprite.0.palette.0.location.0 as u8); object_inner.attrs.a0.set_shape(shape_size.0); object_inner.attrs.a1a.set_size(shape_size.1); object_inner.attrs.a1s.set_size(shape_size.1); - let mut sprite = unsafe { core::mem::transmute(sprite) }; - core::mem::swap(&mut object_inner.sprite, &mut sprite); - sprite.drop(&mut sprite_controller); + object_inner.sprite = sprite; } /// Shows the sprite. No change will be seen until @@ -1027,15 +1017,6 @@ impl<'a> Object<'a> { #[derive(Clone, Copy, PartialEq, Eq, Hash)] struct SpriteId(usize); -impl SpriteId { - fn sprite(self) -> &'static Sprite { - // # Safety - // This must be constructed using the id() of a sprite, so - // they are always valid and always static - unsafe { (self.0 as *const Sprite).as_ref().unwrap_unchecked() } - } -} - /// The palette id is a thin wrapper around the pointer to the palette in rom /// and is therefore a unique reference to a palette #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -1076,145 +1057,76 @@ impl Sprite { } impl SpriteControllerInner { - fn try_get_sprite<'a>( - &mut self, - sprite: &'static Sprite, - controller_reference: ObjectControllerReference<'a>, - ) -> Option> { + fn try_get_sprite<'a>(&mut self, sprite: &'static Sprite) -> Option { let id = sprite.id(); - if let Some(storage) = self.sprite.get_mut(&id) { - storage.count += 1; - let location = storage.location; - let palette_location = self.palette(sprite.palette).unwrap(); - Some(SpriteBorrow { - id, - palette_location, - sprite_location: location, - controller: controller_reference, - }) - } else { - // layout is non zero sized, so this is safe to call - - let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; - - let palette_location = self.palette(sprite.palette); - let palette_location = match palette_location { - Some(a) => a, - None => { - unsafe { SPRITE_ALLOCATOR.dealloc(dest.as_ptr(), sprite.layout()) } - return None; - } - }; - - unsafe { - dma::dma_copy16( - sprite.data.as_ptr().cast(), - dest.as_ptr().cast(), - sprite.data.len() / 2, - ); + if let Some(storage) = self.static_sprite_map.get_mut(&id) { + if let Some(strong) = storage.upgrade() { + return Some(SpriteBorrow { + sprite: SpriteVram(strong), + }); } - - let storage = Storage::from_sprite_ptr(dest); - self.sprite.insert(id, storage); - - Some(SpriteBorrow { - id, - palette_location, - sprite_location: storage.location, - controller: controller_reference, - }) } - } -} -impl SpriteControllerInner { + // layout is non zero sized, so this is safe to call + + let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; + + let palette_location = self.palette(sprite.palette); + let palette_location = match palette_location { + Some(a) => a, + None => { + unsafe { SPRITE_ALLOCATOR.dealloc(dest.as_ptr(), sprite.layout()) } + return None; + } + }; + + unsafe { + dma::dma_copy16( + sprite.data.as_ptr().cast(), + dest.as_ptr().cast(), + sprite.data.len() / 2, + ); + } + + let sprite = SpriteVram(Rc::new(SpriteArena { + location: Location::from_sprite_ptr(dest), + size: sprite.size(), + palette: palette_location, + })); + + self.static_sprite_map.insert(id, Rc::downgrade(&sprite.0)); + + Some(SpriteBorrow { sprite }) + } + fn new() -> Self { - Self { - palette: HashMap::default(), - sprite: HashMap::default(), - } + Default::default() } - fn palette(&mut self, palette: &'static Palette16) -> Option { + fn palette(&mut self, palette: &'static Palette16) -> Option { let id = palette.id(); - if let Some(storage) = self.palette.get_mut(&id) { - storage.count += 1; - Some(storage.location) - } else { - let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; - - unsafe { - dma::dma_copy16( - palette.colours.as_ptr().cast(), - dest.as_ptr().cast(), - palette.colours.len(), - ); - } - - let storage = Storage::from_palette_ptr(dest); - self.palette.insert(id, storage); - - Some(storage.location) - } - } - - fn return_sprite(&mut self, sprite: &'static Sprite) { - let storage = self.sprite.get_mut(&sprite.id()); - - if let Some(storage) = storage { - storage.count -= 1; - - if storage.count == 0 { - unsafe { SPRITE_ALLOCATOR.dealloc(storage.as_sprite_ptr(), sprite.layout()) }; - self.sprite.remove(&sprite.id()); + if let Some(storage) = self.static_palette_map.get(&id) { + if let Some(up) = storage.upgrade() { + return Some(PaletteVram(up)); } } - self.return_palette(sprite.palette); - } + let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; - fn return_palette(&mut self, palette: &'static Palette16) { - let id = palette.id(); - - if let Some(storage) = self.palette.get_mut(&id) { - storage.count -= 1; - - if storage.count == 0 { - unsafe { PALETTE_ALLOCATOR.dealloc(storage.as_palette_ptr(), Palette16::layout()) }; - self.palette.remove(&id); - } + unsafe { + dma::dma_copy16( + palette.colours.as_ptr().cast(), + dest.as_ptr().cast(), + palette.colours.len(), + ); } - } -} -impl<'a> Drop for SpriteBorrow<'a> { - fn drop(&mut self) { - let mut s = unsafe { self.controller.borrow_mut() }; - s.sprite_controller.return_sprite(self.id.sprite()); - } -} + let storage = PaletteVram(Rc::new(PaletteData { + location: Location::from_palette_ptr(dest), + })); + self.static_palette_map + .insert(id, Rc::downgrade(&storage.0)); -impl<'a> SpriteBorrow<'a> { - fn drop(self, s: &mut SpriteControllerInner) { - s.return_sprite(self.id.sprite()); - core::mem::forget(self); - } - - fn clone(&self, s: &mut SpriteControllerInner) -> Self { - s.sprite.entry(self.id).and_modify(|a| a.count += 1); - let _ = s.palette(self.id.sprite().palette).unwrap(); - Self { - id: self.id, - sprite_location: self.sprite_location, - palette_location: self.palette_location, - controller: self.controller, - } - } -} - -impl<'a> Clone for SpriteBorrow<'a> { - fn clone(&self) -> Self { - let mut s = unsafe { self.controller.borrow_mut() }; - self.clone(&mut s.sprite_controller) + Some(storage) } } From 5d541631ae18731d067291bc647e6ab82713a1ac Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 17:56:52 +0100 Subject: [PATCH 06/11] add dynamic sprite support --- agb-image-converter/src/lib.rs | 12 ++-- agb/src/display/object.rs | 116 +++++++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 25 deletions(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 1cd8356b..68260644 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -154,11 +154,13 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { let width = f.width; let height = f.height; quote! { - Sprite::new( - &PALETTES[#assignment], - align_bytes!(u16, #data), - Size::from_width_height(#width, #height) - ) + unsafe { + Sprite::new( + &PALETTES[#assignment], + align_bytes!(u16, #data), + Size::from_width_height(#width, #height) + ) + } } }); diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index a2b9c250..49006c55 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -108,6 +108,50 @@ pub struct Sprite { size: Size, } +/// Sprite data that can be used to create sprites in vram. +pub struct DynamicSprite<'a> { + data: &'a [u8], + size: Size, +} + +impl DynamicSprite<'_> { + #[must_use] + /// Creates a new dynamic sprite from underlying bytes. Note that despite + /// being an array of u8, this must be aligned to at least a 2 byte + /// boundary. + pub fn new(data: &[u8], size: Size) -> DynamicSprite { + let ptr = &data[0] as *const _ as usize; + if ptr % 2 != 0 { + panic!("data is not aligned to a 2 byte boundary"); + } + if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP { + panic!( + "data is not of expected length, got {} expected {}", + data.len(), + size.number_of_tiles() * BYTES_PER_TILE_4BPP + ); + } + DynamicSprite { data, size } + } + + #[must_use] + /// Tries to copy the sprite to vram to be used to set object sprites. + /// Returns None if there is no room in sprite vram. + pub fn try_vram(&self, palette: PaletteVram) -> Option { + Some(SpriteBorrow { + sprite: unsafe { SpriteVram::new(self.data, self.size, palette)? }, + }) + } + + #[must_use] + /// Tries to copy the sprite to vram to be used to set object sprites. + /// Panics if there is no room in sprite vram. + pub fn to_vram(&self, palette: PaletteVram) -> SpriteBorrow { + self.try_vram(palette) + .expect("No slot for sprite available") + } +} + /// The sizes of sprite supported by the GBA. #[derive(Clone, Copy, PartialEq, Eq)] #[allow(missing_docs)] @@ -522,7 +566,9 @@ impl Location { } #[derive(Clone)] -struct PaletteVram(Rc); +/// The palette data in Vram, this is reference counted and the palette data is +/// removed and can be reused from vram when no strong references remain. +pub struct PaletteVram(Rc); #[derive(Clone)] struct SpriteVram(Rc); @@ -531,6 +577,45 @@ struct PaletteData { location: Location, } +impl PaletteVram { + /// Creates a palette in vram from the given palette. Can be used to create + /// sprites in vram in the [DynamicSprite] functions. + #[must_use] + pub fn new(palette: &Palette16) -> Option { + let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; + + unsafe { + dma::dma_copy16( + palette.colours.as_ptr().cast(), + dest.as_ptr().cast(), + palette.colours.len(), + ); + } + + Some(PaletteVram(Rc::new(PaletteData { + location: Location::from_palette_ptr(dest), + }))) + } +} + +impl SpriteVram { + /// # Safety + /// data should be aligned to a 2 byte boundary + unsafe fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option { + let dest = unsafe { SPRITE_ALLOCATOR.alloc(size.layout())? }; + + unsafe { + dma::dma_copy16(data.as_ptr().cast(), dest.as_ptr().cast(), data.len() / 2); + } + + Some(SpriteVram(Rc::new(SpriteArena { + location: Location::from_sprite_ptr(dest), + size, + palette, + }))) + } +} + impl Drop for PaletteData { fn drop(&mut self) { unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) }; @@ -1041,8 +1126,11 @@ impl Sprite { #[doc(hidden)] /// Creates a sprite from it's constituent data, used internally by /// [include_aseprite] and should generally not be used outside it. + /// + /// # Safety + /// The data should be aligned to a 2 byte boundary #[must_use] - pub const fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self { + pub const unsafe fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self { Self { palette, data, @@ -1057,7 +1145,7 @@ impl Sprite { } impl SpriteControllerInner { - fn try_get_sprite<'a>(&mut self, sprite: &'static Sprite) -> Option { + fn try_get_sprite(&mut self, sprite: &'static Sprite) -> Option { let id = sprite.id(); if let Some(storage) = self.static_sprite_map.get_mut(&id) { if let Some(strong) = storage.upgrade() { @@ -1069,17 +1157,16 @@ impl SpriteControllerInner { // layout is non zero sized, so this is safe to call - let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; - let palette_location = self.palette(sprite.palette); let palette_location = match palette_location { Some(a) => a, None => { - unsafe { SPRITE_ALLOCATOR.dealloc(dest.as_ptr(), sprite.layout()) } return None; } }; + let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; + unsafe { dma::dma_copy16( sprite.data.as_ptr().cast(), @@ -1110,23 +1197,12 @@ impl SpriteControllerInner { } } - let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; + let palette_vram = PaletteVram::new(palette)?; - unsafe { - dma::dma_copy16( - palette.colours.as_ptr().cast(), - dest.as_ptr().cast(), - palette.colours.len(), - ); - } - - let storage = PaletteVram(Rc::new(PaletteData { - location: Location::from_palette_ptr(dest), - })); self.static_palette_map - .insert(id, Rc::downgrade(&storage.0)); + .insert(id, Rc::downgrade(&palette_vram.0)); - Some(storage) + Some(palette_vram) } } From b7448d714e8ef5c0760e067c01e2577fac1b39dc Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 18:37:48 +0100 Subject: [PATCH 07/11] remove unused arena --- agb/src/arena.rs | 100 ----------------------------------------------- agb/src/lib.rs | 1 - 2 files changed, 101 deletions(-) delete mode 100644 agb/src/arena.rs diff --git a/agb/src/arena.rs b/agb/src/arena.rs deleted file mode 100644 index 034ac3ff..00000000 --- a/agb/src/arena.rs +++ /dev/null @@ -1,100 +0,0 @@ -use alloc::vec::Vec; - -pub struct Arena { - first: Option, - data: Vec>, -} -enum Item { - Next { - next: Option, - generation: usize, - }, - Item { - item: T, - generation: usize, - }, -} - -#[derive(Debug, Clone, Copy)] -pub struct Index { - generation: usize, - index: usize, -} - -impl Arena { - #[must_use] - pub fn new() -> Self { - Arena { - first: None, - data: Vec::new(), - } - } - pub fn remove(&mut self, idx: Index) { - if let Item::Item { - item: _, - generation, - } = self.data[idx.index] - { - if generation != idx.generation { - return; - } - - self.data[idx.index] = Item::Next { - next: self.first, - generation, - }; - self.first = Some(idx.index); - } - } - pub fn get_mut(&mut self, idx: Index) -> Option<&mut T> { - match &mut self.data[idx.index] { - Item::Next { - next: _, - generation: _, - } => None, - Item::Item { item, generation } => { - if *generation == idx.generation { - Some(item) - } else { - None - } - } - } - } - pub fn insert(&mut self, data: T) -> Index { - match self.first { - Some(idx) => { - let (next, generation) = match &self.data[idx] { - Item::Next { next, generation } => (*next, *generation), - _ => unreachable!(), - }; - self.data[idx] = Item::Item { - item: data, - generation: generation + 1, - }; - - self.first = next; - Index { - generation: generation + 1, - index: idx, - } - } - None => { - self.data.push(Item::Item { - item: data, - generation: 0, - }); - Index { - generation: 0, - index: self.data.len() - 1, - } - } - } - } -} - -impl Default for Arena { - fn default() -> Self { - Self::new() - } -} diff --git a/agb/src/lib.rs b/agb/src/lib.rs index e80f0c70..764cdbfa 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -164,7 +164,6 @@ mod memory_mapped; pub mod mgba; #[doc(inline)] pub use agb_fixnum as fixnum; -pub mod arena; /// Contains an implementation of a hashmap which suits the gameboy advance's hardware. pub mod hash_map; /// Simple random number generator From 71436214071afb73b49513618a3a02c5301ff19e Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 18:56:15 +0100 Subject: [PATCH 08/11] use unsafe around creating new static sprites, no guarentee that it does it correctly though --- agb/examples/chicken.rs | 114 ++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 02536887..cc028776 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -257,62 +257,64 @@ fn handle_collision( static CHICKEN_PALETTE: Palette16 = Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); -static CHICKEN_SPRITES: &[Sprite] = &[ - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x10, 0x01, 0x00, - ], - Size::S8x8, - ), - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, - 0x10, 0x00, 0x00, 0x00, - ], - Size::S8x8, - ), - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00, - 0x00, 0x00, 0x10, 0x00, - ], - Size::S8x8, - ), - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, - 0x00, 0x10, 0x00, 0x00, - ], - Size::S8x8, - ), - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - Size::S8x8, - ), - Sprite::new( - &CHICKEN_PALETTE, - &[ - 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, - 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, - ], - Size::S8x8, - ), -]; +static CHICKEN_SPRITES: &[Sprite] = unsafe { + &[ + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x01, 0x00, + ], + Size::S8x8, + ), + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, + 0x10, 0x00, 0x00, 0x00, + ], + Size::S8x8, + ), + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00, + 0x00, 0x00, 0x10, 0x00, + ], + Size::S8x8, + ), + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, + 0x00, 0x10, 0x00, 0x00, + ], + Size::S8x8, + ), + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + Size::S8x8, + ), + Sprite::new( + &CHICKEN_PALETTE, + &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + Size::S8x8, + ), + ] +}; static MAP_TILES: [u8; 8 * 17 * 4] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, From 5e0795ae1424b278d8590d31ea22237ab8e69716 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Oct 2022 20:05:13 +0100 Subject: [PATCH 09/11] add garbage collection using hashmap retain --- agb/examples/sprites.rs | 3 +-- agb/src/display/object.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 0ae5ffb5..c5ea5ba8 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -46,9 +46,8 @@ fn all_sprites(gfx: &ObjectController) { if count % 5 == 0 { image += 1; image %= SPRITES.len(); - let objs_len = objs.len(); for (i, obj) in objs.iter_mut().enumerate() { - let this_image = (image + i * SPRITES.len() / objs_len) % SPRITES.len(); + let this_image = (image + i) % SPRITES.len(); obj.set_sprite(gfx.sprite(&SPRITES[this_image])); } gfx.commit(); diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 49006c55..2b6eaf23 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -741,6 +741,8 @@ impl ObjectController { } } } + + s.sprite_controller.gc(); } pub(crate) fn new() -> Self { @@ -1186,6 +1188,12 @@ impl SpriteControllerInner { Some(SpriteBorrow { sprite }) } + /// Cleans up weak references to sprites and palettes no longer in vram + fn gc(&mut self) { + self.static_palette_map.retain(|_, v| v.strong_count() != 0); + self.static_sprite_map.retain(|_, v| v.strong_count() != 0); + } + fn new() -> Self { Default::default() } From 0a715c347c6a7903c34263993398df0dcadc6518 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 8 Oct 2022 23:38:58 +0100 Subject: [PATCH 10/11] write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5809d563..b1db0f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added implementation of `HashMap.retain()`. - Added support for affine backgrounds (tiled modes 1 and 2) which allows for scaling, rotating etc of tiled backgrounds. - Added support for 256 colour backgrounds (when working with affine ones). +- Added support for dynamic sprites generated at runtime, some parts of this may change significantly so breaking changes are expected here. ### Changes - Many of the places that originally disabled IRQs now use the `sync` module, reducing the chance of missed interrupts. From dead6652ebfa20e9de52d7c79a997f8cfbf5f420 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 8 Oct 2022 23:46:54 +0100 Subject: [PATCH 11/11] fix lint --- agb/src/display/object.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 8ba21cbf..01771931 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -558,10 +558,10 @@ impl Location { Self((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) } fn as_palette_ptr(self) -> *mut u8 { - (self.0 as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 + (self.0 * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 } fn as_sprite_ptr(self) -> *mut u8 { - (self.0 as usize * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 + (self.0 * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 } }