copy affine matricies at copy time

This commit is contained in:
Corwin 2023-04-03 22:29:59 +01:00
parent 354cf7addc
commit 0e9910c517
No known key found for this signature in database
5 changed files with 74 additions and 105 deletions

View file

@ -12,8 +12,6 @@ pub use affine::AffineMatrix;
pub use managed::{OAMManager, Object}; pub use managed::{OAMManager, Object};
pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject};
pub(crate) use affine::init_affine;
use super::DISPLAY_CONTROL; use super::DISPLAY_CONTROL;
const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000;

View file

@ -1,37 +1,63 @@
use core::cell::Cell;
use alloc::rc::Rc; use alloc::rc::Rc;
use crate::{display::affine::AffineMatrixObject, sync::Static}; use crate::display::affine::AffineMatrixObject;
use super::OBJECT_ATTRIBUTE_MEMORY; use super::OBJECT_ATTRIBUTE_MEMORY;
#[derive(Debug)] #[derive(Debug)]
struct AffineMatrixLocation { struct AffineMatrixData {
location: u16, frame_count: Cell<u32>,
location: Cell<u32>,
matrix: AffineMatrixObject,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AffineMatrixVram(Rc<AffineMatrixLocation>); pub(crate) struct AffineMatrixVram(Rc<AffineMatrixData>);
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct AffineMatrix { pub struct AffineMatrix {
location: AffineMatrixVram, location: AffineMatrixVram,
} }
impl AffineMatrix { impl AffineMatrix {
pub fn new(affine_matrix: AffineMatrixObject) -> Option<AffineMatrix> { #[must_use]
let mut matrix = AFFINE_MATRIX_DISTRIBUTOR.get_matrix()?; pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrix {
matrix.write(affine_matrix); AffineMatrix {
location: AffineMatrixVram(Rc::new(AffineMatrixData {
Some(matrix) frame_count: Cell::new(u32::MAX),
location: Cell::new(u32::MAX),
matrix: affine_matrix,
})),
}
} }
pub(crate) fn vram(self) -> AffineMatrixVram { pub(crate) fn vram(self) -> AffineMatrixVram {
self.location self.location
} }
}
fn write(&mut self, affine_matrix: AffineMatrixObject) { impl AffineMatrixVram {
let components = affine_matrix.components(); pub fn frame_count(&self) -> u32 {
let location = self.location.0.location as usize; self.0.frame_count.get()
}
pub fn set_frame_count(&self, frame: u32) {
self.0.frame_count.set(frame);
}
pub fn location(&self) -> u32 {
self.0.location.get()
}
pub fn set_location(&self, location: u32) {
self.0.location.set(location);
}
pub fn write_to_location(&self) {
let components = self.0.matrix.components();
let location = self.0.location.get() as usize;
for (idx, component) in components.iter().enumerate() { for (idx, component) in components.iter().enumerate() {
unsafe { unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16) (OBJECT_ATTRIBUTE_MEMORY as *mut u16)
@ -42,74 +68,15 @@ impl AffineMatrix {
} }
} }
impl AffineMatrixVram { #[cfg(test)]
pub fn location(&self) -> u16 { mod tests {
self.0.location use super::*;
}
} #[test_case]
fn niche_optimisation(_gba: &mut crate::Gba) {
impl Drop for AffineMatrixLocation { assert_eq!(
fn drop(&mut self) { core::mem::size_of::<AffineMatrix>(),
// safety: obtained via affine matrix distributor core::mem::size_of::<Option<AffineMatrix>>()
unsafe { AFFINE_MATRIX_DISTRIBUTOR.return_matrix(self.location) } );
}
}
struct AffineMatrixDistributor {
tip: Static<u16>,
}
static AFFINE_MATRIX_DISTRIBUTOR: AffineMatrixDistributor = AffineMatrixDistributor::new();
pub(crate) unsafe fn init_affine() {
AFFINE_MATRIX_DISTRIBUTOR.initialise_affine_matricies();
}
impl AffineMatrixDistributor {
const fn new() -> Self {
AffineMatrixDistributor {
tip: Static::new(u16::MAX),
}
}
unsafe fn initialise_affine_matricies(&self) {
for i in 0..32 {
let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 16 + 3);
if i == 31 {
// none
ptr.write_volatile(u16::MAX);
} else {
ptr.write_volatile(i as u16 + 1);
}
}
self.tip.write(0);
}
fn location_of(affine_matrix_location: u16) -> *mut u16 {
unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(affine_matrix_location as usize * 16 + 3)
}
}
fn get_matrix(&self) -> Option<AffineMatrix> {
let location = self.tip.read();
if location == u16::MAX {
return None;
}
let next_tip = unsafe { Self::location_of(location).read_volatile() };
self.tip.write(next_tip);
Some(AffineMatrix {
location: AffineMatrixVram(Rc::new(AffineMatrixLocation { location })),
})
}
unsafe fn return_matrix(&self, mat_id: u16) {
Self::location_of(mat_id).write_volatile(mat_id);
self.tip.write(mat_id);
} }
} }

View file

@ -6,7 +6,7 @@ use self::attributes::{
ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2, ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2,
}; };
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct Attributes { pub struct Attributes {
a0: ObjectAttribute0, a0: ObjectAttribute0,
a1s: ObjectAttribute1Standard, a1s: ObjectAttribute1Standard,
@ -32,7 +32,7 @@ pub enum AffineMode {
} }
impl Attributes { impl Attributes {
pub fn bytes(&self) -> [u8; 6] { pub fn bytes(self) -> [u8; 6] {
let mode = self.a0.object_mode(); let mode = self.a0.object_mode();
let attrs = match mode { let attrs = match mode {
ObjectMode::Normal => [ ObjectMode::Normal => [
@ -51,7 +51,7 @@ impl Attributes {
unsafe { core::mem::transmute(attrs) } unsafe { core::mem::transmute(attrs) }
} }
pub fn is_visible(&self) -> bool { pub fn is_visible(self) -> bool {
self.a0.object_mode() != ObjectMode::Disabled self.a0.object_mode() != ObjectMode::Disabled
} }

View file

@ -16,14 +16,14 @@ use super::attributes::{AffineMode, Attributes};
struct OamFrameModifyables { struct OamFrameModifyables {
up_to: i32, up_to: i32,
this_frame_sprites: Vec<SpriteVram>, this_frame_sprites: Vec<SpriteVram>,
this_frame_affine: Vec<AffineMatrixVram>, frame: u32,
affine_matrix_count: u32,
} }
pub struct UnmanagedOAM<'gba> { pub struct UnmanagedOAM<'gba> {
phantom: PhantomData<&'gba ()>, phantom: PhantomData<&'gba ()>,
frame_data: UnsafeCell<OamFrameModifyables>, frame_data: UnsafeCell<OamFrameModifyables>,
previous_frame_sprites: Vec<SpriteVram>, previous_frame_sprites: Vec<SpriteVram>,
previous_frame_affine: Vec<AffineMatrixVram>,
} }
pub struct OAMIterator<'oam> { pub struct OAMIterator<'oam> {
@ -38,16 +38,29 @@ pub struct OAMSlot<'oam> {
impl OAMSlot<'_> { impl OAMSlot<'_> {
pub fn set(&mut self, object: &UnmanagedObject) { pub fn set(&mut self, object: &UnmanagedObject) {
self.set_bytes(object.attributes.bytes()); let mut attributes = object.attributes;
// SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM].
let frame_data = unsafe { &mut *self.frame_data.get() }; let frame_data = unsafe { &mut *self.frame_data.get() };
frame_data.this_frame_sprites.push(object.sprite.clone()); if let Some(affine_matrix) = &object.affine_matrix {
if let Some(affine) = &object.affine_matrix { if affine_matrix.frame_count() != frame_data.frame {
frame_data.this_frame_affine.push(affine.clone()); affine_matrix.set_frame_count(frame_data.frame);
assert!(
frame_data.affine_matrix_count <= 32,
"too many affine matricies in one frame"
);
affine_matrix.set_location(frame_data.affine_matrix_count);
frame_data.affine_matrix_count += 1;
affine_matrix.write_to_location();
}
attributes.set_affine_matrix(affine_matrix.location() as u16);
} }
self.set_bytes(attributes.bytes());
frame_data.this_frame_sprites.push(object.sprite.clone());
frame_data.up_to = self.slot as i32; frame_data.up_to = self.slot as i32;
} }
@ -96,6 +109,8 @@ impl UnmanagedOAM<'_> {
pub fn iter(&mut self) -> OAMIterator<'_> { pub fn iter(&mut self) -> OAMIterator<'_> {
let frame_data = self.frame_data.get_mut(); let frame_data = self.frame_data.get_mut();
frame_data.up_to = -1; frame_data.up_to = -1;
frame_data.frame = frame_data.frame.wrapping_add(1);
frame_data.affine_matrix_count = 0;
// We drain the previous frame sprites here to reuse the Vecs allocation and remove the now unused sprites. // We drain the previous frame sprites here to reuse the Vecs allocation and remove the now unused sprites.
// Any sprites currently being shown will now be put in the new Vec. // Any sprites currently being shown will now be put in the new Vec.
@ -105,13 +120,6 @@ impl UnmanagedOAM<'_> {
&mut self.previous_frame_sprites, &mut self.previous_frame_sprites,
); );
// Drain previous frame affine matricies
self.previous_frame_affine.drain(..);
core::mem::swap(
&mut frame_data.this_frame_affine,
&mut self.previous_frame_affine,
);
OAMIterator { OAMIterator {
index: 0, index: 0,
frame_data: &self.frame_data, frame_data: &self.frame_data,
@ -123,7 +131,6 @@ impl UnmanagedOAM<'_> {
frame_data: Default::default(), frame_data: Default::default(),
phantom: PhantomData, phantom: PhantomData,
previous_frame_sprites: Default::default(), previous_frame_sprites: Default::default(),
previous_frame_affine: Default::default(),
} }
} }
} }
@ -221,9 +228,7 @@ impl UnmanagedObject {
pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self {
let vram = affine_matrix.vram(); let vram = affine_matrix.vram();
let location = vram.location();
self.affine_matrix = Some(vram); self.affine_matrix = Some(vram);
self.attributes.set_affine_matrix(location);
self self
} }

View file

@ -236,7 +236,6 @@ impl Gba {
#[doc(hidden)] #[doc(hidden)]
#[must_use] #[must_use]
pub unsafe fn new_in_entry() -> Self { pub unsafe fn new_in_entry() -> Self {
display::object::init_affine();
Self::single_new() Self::single_new()
} }