affines are referenced counted with the new RC arena(ish) implementation

This commit is contained in:
Corwin Kuiper 2021-10-16 16:46:02 +01:00
parent e92383e29c
commit 8d0c50b37a
3 changed files with 169 additions and 60 deletions

140
agb/src/arena.rs Normal file
View file

@ -0,0 +1,140 @@
use core::{cell::RefCell, num::NonZeroU8};
type Index = u8;
pub struct Loan<'a, const S: usize> {
pub my_index: Index,
arena: &'a RefCell<ArenaInner<S>>,
}
/// Next should be an option, however this raises this struct to 3 bytes where
/// it is now only 2 bytes This saves a byte per entry and therefore it is
/// probably worth it.
#[derive(Clone, Copy, Debug)]
enum Element {
Contains(NonZeroU8),
Next(Index),
}
#[derive(Debug)]
struct ArenaInner<const S: usize> {
arena: [Element; S],
first: Index,
}
pub struct Arena<const S: usize> {
arena_inner: RefCell<ArenaInner<S>>,
}
impl<const S: usize> Arena<S> {
pub fn new() -> Arena<S> {
// we use the special value u8::MAX as a None
assert!(S < u8::MAX as usize - 1);
let mut arena: [Element; S] = [Element::Next(u8::MAX); S];
arena
.iter_mut()
.enumerate()
.for_each(|(index, e)| *e = Element::Next(index as Index + 1));
arena.last_mut().map(|a| *a = Element::Next(u8::MAX));
Arena {
arena_inner: RefCell::new(ArenaInner { arena, first: 0 }),
}
}
pub fn get_next_free(&self) -> Option<Loan<S>> {
let mut arena = self.arena_inner.borrow_mut();
let i = arena.first;
if i == u8::MAX {
return None;
}
if let Element::Next(n) = arena.arena[i as usize] {
arena.first = n;
} else {
unreachable!("invalid state, next points to already occupied state");
}
arena.arena[i as usize] = Element::Contains(NonZeroU8::new(1).unwrap());
Some(Loan {
my_index: i,
arena: &self.arena_inner,
})
}
}
impl<const S: usize> Drop for Loan<'_, S> {
fn drop(&mut self) {
let mut arena = self.arena.borrow_mut();
let me = &mut arena.arena[self.my_index as usize];
match me {
Element::Contains(n) => {
let mut a = n.get();
a -= 1;
if a == 0 {
arena.arena[self.my_index as usize] = Element::Next(arena.first);
arena.first = self.my_index;
} else {
*n = NonZeroU8::new(a).unwrap();
}
}
_ => unreachable!("if a loan exists the correspoinding arena entry should be filled"),
}
}
}
impl<const S: usize> Clone for Loan<'_, S> {
fn clone(&self) -> Self {
match &mut self.arena.borrow_mut().arena[self.my_index as usize] {
Element::Contains(n) => {
let a = n.get();
*n = NonZeroU8::new(a + 1).unwrap();
}
_ => unreachable!("if a loan exists the correspoinding arena entry should be filled"),
}
Loan {
my_index: self.my_index,
arena: self.arena,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc;
#[test_case]
fn size_of_element(_gba: &mut crate::Gba) {
let s = core::mem::size_of::<Element>();
assert_eq!(s, 2, "elements should be of a minimum size");
}
#[test_case]
fn get_everything(_gba: &mut crate::Gba) {
let s: Arena<4> = Arena::new();
{
let mut c = alloc::vec::Vec::new();
c.push(s.get_next_free());
c.push(s.get_next_free());
c.iter().for_each(|a| assert!(a.is_some()));
}
{
let mut c = alloc::vec::Vec::new();
c.push(s.get_next_free());
c.push(s.get_next_free());
c.push(s.get_next_free());
c.push(s.get_next_free());
c.iter().for_each(|a| assert!(a.is_some()));
assert!(s.get_next_free().is_none());
}
{
let mut c = alloc::vec::Vec::new();
c.push(s.get_next_free());
c.push(s.get_next_free());
c.push(s.get_next_free());
c.push(s.get_next_free());
c.iter().for_each(|a| assert!(a.is_some()));
assert!(s.get_next_free().is_none());
}
}
}

View file

@ -5,6 +5,9 @@ use crate::bitarray::Bitarray;
use crate::memory_mapped::MemoryMapped1DArray;
use crate::number::Vector2D;
type AffineLoan<'a> = crate::arena::Loan<'a, 32>;
type AffineArena = crate::arena::Arena<32>;
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
@ -15,7 +18,7 @@ const TILE_SPRITE: MemoryMapped1DArray<u32, { 512 * 8 }> =
/// Handles distributing objects and matricies along with operations that effect all objects.
pub struct ObjectControl {
objects: RefCell<Bitarray<4>>,
affines: RefCell<Bitarray<1>>,
affines: AffineArena,
}
struct ObjectLoan<'a> {
@ -23,11 +26,6 @@ struct ObjectLoan<'a> {
objects: &'a RefCell<Bitarray<4>>,
}
struct AffineLoan<'a> {
index: u8,
affines: &'a RefCell<Bitarray<1>>,
}
/// The standard object, without rotation.
pub struct ObjectStandard<'a> {
attributes: ObjectAttribute,
@ -39,7 +37,7 @@ pub struct ObjectStandard<'a> {
pub struct ObjectAffine<'a> {
attributes: ObjectAttribute,
loan: ObjectLoan<'a>,
aff_id: Option<u8>,
aff_loan: Option<AffineLoan<'a>>,
}
/// Refers to an affine matrix in the OAM. Includes both an index and the
@ -161,7 +159,7 @@ impl<'a> ObjectAffine<'a> {
/// Show the object on screen. Panics if affine matrix has not been set.
pub fn show(&mut self) {
if self.aff_id.is_none() {
if self.aff_loan.is_none() {
panic!("affine matrix should be set")
}
self.attributes.set_mode(Mode::Affine)
@ -170,11 +168,12 @@ impl<'a> ObjectAffine<'a> {
pub fn hide(&mut self) {
self.attributes.set_mode(Mode::Hidden)
}
/// Sets the affine matrix to use. Changing the affine matrix will change
/// how the sprite is rendered.
pub fn set_affine_mat(&mut self, aff: &AffineMatrix) {
self.attributes.set_affine(aff.loan.index);
self.aff_id = Some(aff.loan.index);
pub fn set_affine_mat(&mut self, aff: &AffineMatrix<'a>) {
self.attributes.set_affine(aff.loan.my_index);
self.aff_loan = Some(aff.loan.clone());
}
/// Sets the x and y position of the object, performing casts as nessesary
@ -207,22 +206,6 @@ impl Drop for ObjectLoan<'_> {
}
}
impl Drop for AffineLoan<'_> {
fn drop(&mut self) {
let attributes = AffineMatrixAttributes {
p_a: 0,
p_b: 0,
p_c: 0,
p_d: 0,
};
unsafe {
attributes.commit(self.index);
}
let mut affs = self.affines.borrow_mut();
affs.set(self.index as usize, false);
}
}
struct ObjectAttribute {
a0: u16,
a1: u16,
@ -276,7 +259,7 @@ impl ObjectAttribute {
impl AffineMatrix<'_> {
/// Commits matrix to OAM, will cause any objects using this matrix to be updated.
pub fn commit(&self) {
unsafe { self.attributes.commit(self.loan.index) };
unsafe { self.attributes.commit(self.loan.my_index) };
}
}
@ -311,7 +294,7 @@ impl ObjectControl {
}
ObjectControl {
objects: RefCell::new(Bitarray::new()),
affines: RefCell::new(Bitarray::new()),
affines: AffineArena::new(),
}
}
@ -373,17 +356,6 @@ impl ObjectControl {
panic!("object id must be less than 128");
}
fn get_unused_affine_index(&self) -> u8 {
let mut affines = self.affines.borrow_mut();
for index in 0..32 {
if !affines.get(index).unwrap() {
affines.set(index, true);
return index as u8;
}
}
panic!("affine id must be less than 32");
}
/// Get an unused standard object. Panics if more than 128 objects are
/// obtained.
pub fn get_object_standard(&self) -> ObjectStandard {
@ -407,14 +379,13 @@ impl ObjectControl {
objects: &self.objects,
index: id,
},
aff_id: None,
aff_loan: None,
}
}
/// Get an unused affine matrix. Panics if more than 32 affine matricies are
/// obtained.
pub fn get_affine(&self) -> AffineMatrix {
let id = self.get_unused_affine_index();
AffineMatrix {
attributes: AffineMatrixAttributes {
p_a: 0,
@ -422,10 +393,10 @@ impl ObjectControl {
p_c: 0,
p_d: 0,
},
loan: AffineLoan {
affines: &self.affines,
index: id,
},
loan: self
.affines
.get_next_free()
.expect("there are no affines avaliable"),
}
}
}
@ -457,14 +428,14 @@ mod tests {
let _a1 = {
let a0 = objs.get_affine();
let a1 = objs.get_affine();
assert_eq!(a0.loan.index, 0);
assert_eq!(a1.loan.index, 1);
assert_eq!(a0.loan.my_index, 0);
assert_eq!(a1.loan.my_index, 1);
a1
};
let a0 = objs.get_affine();
assert_eq!(a0.loan.index, 0);
assert_eq!(a0.loan.my_index, 0);
let a2 = objs.get_affine();
assert_eq!(a2.loan.index, 2);
assert_eq!(a2.loan.my_index, 2);
}
}

View file

@ -13,31 +13,29 @@
//! internal workings of the Game Boy Advance whilst still being high
//! performance and memory efficient.
pub use agb_image_converter::include_gfx;
pub use agb_macros::entry;
pub use agb_sound_converter::include_wav;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod agb_alloc;
mod arena;
mod bitarray;
/// Implements everything relating to things that are displayed on screen.
pub mod display;
/// Button inputs to the system.
pub mod input;
/// Implements sound output.
pub mod sound;
pub use agb_image_converter::include_gfx;
pub use agb_sound_converter::include_wav;
pub use agb_macros::entry;
mod bitarray;
pub mod interrupt;
mod memory_mapped;
/// Implements logging to the mgba emulator.
pub mod mgba;
pub mod number;
mod single;
/// Implements sound output.
pub mod sound;
/// System BIOS calls / syscalls.
pub mod syscall;