mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
copy affine matricies at copy time
This commit is contained in:
parent
354cf7addc
commit
0e9910c517
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
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);
|
||||
#[test_case]
|
||||
fn niche_optimisation(_gba: &mut crate::Gba) {
|
||||
assert_eq!(
|
||||
core::mem::size_of::<AffineMatrix>(),
|
||||
core::mem::size_of::<Option<AffineMatrix>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -236,7 +236,6 @@ impl Gba {
|
|||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub unsafe fn new_in_entry() -> Self {
|
||||
display::object::init_affine();
|
||||
Self::single_new()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue