mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
various micro optimisations
This commit is contained in:
parent
4f19d6c240
commit
f59e4ad322
|
@ -35,23 +35,26 @@ pub enum AffineMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl Attributes {
|
||||||
pub fn bytes(self) -> [u8; 6] {
|
pub fn write(self, ptr: *mut u16) {
|
||||||
let mode = self.a0.object_mode();
|
let mode = self.a0.object_mode();
|
||||||
let attrs = match mode {
|
unsafe {
|
||||||
ObjectMode::Normal => [
|
let attrs = core::mem::transmute::<_, [u16; 3]>(match mode {
|
||||||
self.a0.into_bytes(),
|
ObjectMode::Normal => [
|
||||||
self.a1s.into_bytes(),
|
self.a0.into_bytes(),
|
||||||
self.a2.into_bytes(),
|
self.a1s.into_bytes(),
|
||||||
],
|
self.a2.into_bytes(),
|
||||||
_ => [
|
],
|
||||||
self.a0.into_bytes(),
|
_ => [
|
||||||
self.a1a.into_bytes(),
|
self.a0.into_bytes(),
|
||||||
self.a2.into_bytes(),
|
self.a1a.into_bytes(),
|
||||||
],
|
self.a2.into_bytes(),
|
||||||
};
|
],
|
||||||
|
});
|
||||||
|
|
||||||
// Safety: length and alignment are the same, and every possible value is valid
|
ptr.add(0).write_volatile(attrs[0]);
|
||||||
unsafe { core::mem::transmute(attrs) }
|
ptr.add(1).write_volatile(attrs[1]);
|
||||||
|
ptr.add(2).write_volatile(attrs[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_visible(self) -> bool {
|
pub fn is_visible(self) -> bool {
|
||||||
|
|
|
@ -13,11 +13,12 @@ use crate::display::{
|
||||||
|
|
||||||
use super::attributes::{AffineMode, Attributes};
|
use super::attributes::{AffineMode, Attributes};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Debug)]
|
||||||
struct OamFrameModifyables {
|
struct OamFrameModifyables {
|
||||||
this_frame_sprites: Vec<SpriteVram>,
|
this_frame_sprites: Vec<SpriteVram>,
|
||||||
frame: u32,
|
frame: u32,
|
||||||
affine_matrix_count: u32,
|
affine_matrix_count: u32,
|
||||||
|
previous_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OamUnmanaged<'gba> {
|
pub struct OamUnmanaged<'gba> {
|
||||||
|
@ -47,60 +48,62 @@ impl Drop for OamSlot<'_> {
|
||||||
|
|
||||||
impl OamSlot<'_> {
|
impl OamSlot<'_> {
|
||||||
/// Set the slot in OAM to contain the sprite given.
|
/// Set the slot in OAM to contain the sprite given.
|
||||||
pub fn set(mut self, object: &ObjectUnmanaged) {
|
#[inline(always)]
|
||||||
let mut attributes = object.attributes;
|
pub fn set(self, object: &ObjectUnmanaged) {
|
||||||
// SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM].
|
self.set_inner(object);
|
||||||
let frame_data = unsafe { &mut *self.frame_data.get() };
|
|
||||||
|
|
||||||
Self::handle_affine(&mut attributes, frame_data, object);
|
|
||||||
self.set_bytes(attributes.bytes());
|
|
||||||
|
|
||||||
frame_data.this_frame_sprites.push(object.sprite.clone());
|
|
||||||
|
|
||||||
// don't call the drop implementation.
|
// don't call the drop implementation.
|
||||||
// okay as none of the fields we have have drop implementations.
|
// okay as none of the fields we have have drop implementations.
|
||||||
core::mem::forget(self);
|
core::mem::forget(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// By writing these as two separate functions, one inlined and one not, the
|
||||||
|
/// compiler doesn't have to copy around the slot structure while still
|
||||||
|
/// keeping move semantics. This is slightly faster in benchmarks.
|
||||||
|
#[inline(never)]
|
||||||
|
fn set_inner(&self, object: &ObjectUnmanaged) {
|
||||||
|
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() };
|
||||||
|
|
||||||
|
if let Some(affine_matrix) = &object.affine_matrix {
|
||||||
|
Self::handle_affine(&mut attributes, frame_data, affine_matrix);
|
||||||
|
}
|
||||||
|
attributes.write(unsafe { (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(self.slot * 4) });
|
||||||
|
|
||||||
|
frame_data.this_frame_sprites.push(object.sprite.clone());
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_affine(
|
fn handle_affine(
|
||||||
attributes: &mut Attributes,
|
attributes: &mut Attributes,
|
||||||
frame_data: &mut OamFrameModifyables,
|
frame_data: &mut OamFrameModifyables,
|
||||||
object: &ObjectUnmanaged,
|
affine_matrix: &AffineMatrixVram,
|
||||||
) {
|
) {
|
||||||
if let Some(affine_matrix) = &object.affine_matrix {
|
if affine_matrix.frame_count() != frame_data.frame {
|
||||||
if affine_matrix.frame_count() != frame_data.frame {
|
affine_matrix.set_frame_count(frame_data.frame);
|
||||||
affine_matrix.set_frame_count(frame_data.frame);
|
assert!(
|
||||||
assert!(
|
frame_data.affine_matrix_count <= 32,
|
||||||
frame_data.affine_matrix_count <= 32,
|
"too many affine matricies in one frame"
|
||||||
"too many affine matricies in one frame"
|
);
|
||||||
);
|
affine_matrix.set_location(frame_data.affine_matrix_count);
|
||||||
affine_matrix.set_location(frame_data.affine_matrix_count);
|
frame_data.affine_matrix_count += 1;
|
||||||
frame_data.affine_matrix_count += 1;
|
affine_matrix.write_to_location(OBJECT_ATTRIBUTE_MEMORY);
|
||||||
affine_matrix.write_to_location(OBJECT_ATTRIBUTE_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes.set_affine_matrix(affine_matrix.location() as u16);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn set_bytes(&mut self, bytes: [u8; 6]) {
|
attributes.set_affine_matrix(affine_matrix.location() as u16);
|
||||||
unsafe {
|
|
||||||
let address = (OBJECT_ATTRIBUTE_MEMORY as *mut u8).add(self.slot * 8);
|
|
||||||
address.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'oam> Iterator for OamIterator<'oam> {
|
impl<'oam> Iterator for OamIterator<'oam> {
|
||||||
type Item = OamSlot<'oam>;
|
type Item = OamSlot<'oam>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let idx = self.index;
|
let idx = self.index;
|
||||||
self.index += 1;
|
if idx == 128 {
|
||||||
|
|
||||||
if idx >= 128 {
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
self.index += 1;
|
||||||
Some(OamSlot {
|
Some(OamSlot {
|
||||||
slot: idx,
|
slot: idx,
|
||||||
frame_data: self.frame_data,
|
frame_data: self.frame_data,
|
||||||
|
@ -112,13 +115,15 @@ impl<'oam> Iterator for OamIterator<'oam> {
|
||||||
impl Drop for OamIterator<'_> {
|
impl Drop for OamIterator<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let number_writen = self.index;
|
let number_writen = self.index;
|
||||||
|
let last_frame_written = unsafe { &mut (*self.frame_data.get()).previous_index };
|
||||||
|
|
||||||
for idx in number_writen..128 {
|
for idx in number_writen..*last_frame_written {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(idx * 4);
|
let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(idx * 4);
|
||||||
ptr.write_volatile(0b10 << 8);
|
ptr.write_volatile(0b10 << 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*last_frame_written = number_writen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +135,7 @@ impl OamUnmanaged<'_> {
|
||||||
|
|
||||||
// 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.
|
||||||
self.previous_frame_sprites.drain(..);
|
self.previous_frame_sprites.clear();
|
||||||
core::mem::swap(
|
core::mem::swap(
|
||||||
&mut frame_data.this_frame_sprites,
|
&mut frame_data.this_frame_sprites,
|
||||||
&mut self.previous_frame_sprites,
|
&mut self.previous_frame_sprites,
|
||||||
|
@ -144,7 +149,12 @@ impl OamUnmanaged<'_> {
|
||||||
|
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
frame_data: Default::default(),
|
frame_data: UnsafeCell::new(OamFrameModifyables {
|
||||||
|
this_frame_sprites: Vec::new(),
|
||||||
|
frame: 0,
|
||||||
|
affine_matrix_count: 0,
|
||||||
|
previous_index: 0,
|
||||||
|
}),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
previous_frame_sprites: Default::default(),
|
previous_frame_sprites: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue