mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
affines are referenced counted with the new RC arena(ish) implementation
This commit is contained in:
parent
e92383e29c
commit
8d0c50b37a
140
agb/src/arena.rs
Normal file
140
agb/src/arena.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue