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 unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject};
pub(crate) use affine::init_affine;
use super::DISPLAY_CONTROL;
const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000;

View file

@ -1,37 +1,63 @@
use core::cell::Cell;
use alloc::rc::Rc;
use crate::{display::affine::AffineMatrixObject, sync::Static};
use crate::display::affine::AffineMatrixObject;
use super::OBJECT_ATTRIBUTE_MEMORY;
#[derive(Debug)]
struct AffineMatrixLocation {
location: u16,
struct AffineMatrixData {
frame_count: Cell<u32>,
location: Cell<u32>,
matrix: AffineMatrixObject,
}
#[derive(Debug, Clone)]
pub(crate) struct AffineMatrixVram(Rc<AffineMatrixLocation>);
pub(crate) struct AffineMatrixVram(Rc<AffineMatrixData>);
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct AffineMatrix {
location: AffineMatrixVram,
}
impl AffineMatrix {
pub fn new(affine_matrix: AffineMatrixObject) -> Option<AffineMatrix> {
let mut matrix = AFFINE_MATRIX_DISTRIBUTOR.get_matrix()?;
matrix.write(affine_matrix);
Some(matrix)
#[must_use]
pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrix {
AffineMatrix {
location: AffineMatrixVram(Rc::new(AffineMatrixData {
frame_count: Cell::new(u32::MAX),
location: Cell::new(u32::MAX),
matrix: affine_matrix,
})),
}
}
pub(crate) fn vram(self) -> AffineMatrixVram {
self.location
}
}
fn write(&mut self, affine_matrix: AffineMatrixObject) {
let components = affine_matrix.components();
let location = self.location.0.location as usize;
impl AffineMatrixVram {
pub fn frame_count(&self) -> u32 {
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() {
unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
@ -42,74 +68,15 @@ impl AffineMatrix {
}
}
impl AffineMatrixVram {
pub fn location(&self) -> u16 {
self.0.location
}
}
impl Drop for AffineMatrixLocation {
fn drop(&mut self) {
// safety: obtained via affine matrix distributor
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);
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn niche_optimisation(_gba: &mut crate::Gba) {
assert_eq!(
core::mem::size_of::<AffineMatrix>(),
core::mem::size_of::<Option<AffineMatrix>>()
);
}
}

View file

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

View file

@ -16,14 +16,14 @@ use super::attributes::{AffineMode, Attributes};
struct OamFrameModifyables {
up_to: i32,
this_frame_sprites: Vec<SpriteVram>,
this_frame_affine: Vec<AffineMatrixVram>,
frame: u32,
affine_matrix_count: u32,
}
pub struct UnmanagedOAM<'gba> {
phantom: PhantomData<&'gba ()>,
frame_data: UnsafeCell<OamFrameModifyables>,
previous_frame_sprites: Vec<SpriteVram>,
previous_frame_affine: Vec<AffineMatrixVram>,
}
pub struct OAMIterator<'oam> {
@ -38,16 +38,29 @@ pub struct OAMSlot<'oam> {
impl OAMSlot<'_> {
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].
let frame_data = unsafe { &mut *self.frame_data.get() };
frame_data.this_frame_sprites.push(object.sprite.clone());
if let Some(affine) = &object.affine_matrix {
frame_data.this_frame_affine.push(affine.clone());
if let Some(affine_matrix) = &object.affine_matrix {
if affine_matrix.frame_count() != frame_data.frame {
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;
}
@ -96,6 +109,8 @@ impl UnmanagedOAM<'_> {
pub fn iter(&mut self) -> OAMIterator<'_> {
let frame_data = self.frame_data.get_mut();
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.
// Any sprites currently being shown will now be put in the new Vec.
@ -105,13 +120,6 @@ impl UnmanagedOAM<'_> {
&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 {
index: 0,
frame_data: &self.frame_data,
@ -123,7 +131,6 @@ impl UnmanagedOAM<'_> {
frame_data: Default::default(),
phantom: PhantomData,
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 {
let vram = affine_matrix.vram();
let location = vram.location();
self.affine_matrix = Some(vram);
self.attributes.set_affine_matrix(location);
self
}

View file

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