use crate::GeneralResolution;
static INDICE_TO_SIZE_MAP: [Option<(u8, u8)>; 16] = [
Some((8, 8)),
Some((16, 16)),
Some((32, 32)),
Some((64, 64)),
Some((16, 8)),
Some((32, 8)),
Some((32, 16)),
Some((64, 32)),
Some((8, 16)),
Some((8, 32)),
Some((16, 32)),
Some((32, 64)),
None,
None,
None,
None,
];
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OamShape {
shape_indice: u8,
size_indice: u8,
}
impl OamShape {
pub fn new(shape_indice: u8, size_indice: u8) -> Option<Self> {
if shape_indice <= 2 && size_indice <= 3 {
Some(Self {
shape_indice,
size_indice,
})
} else {
None
}
}
pub fn shape_indice(&self) -> u8 {
self.shape_indice
}
pub fn size_indice(&self) -> u8 {
self.size_indice
}
pub fn size(&self) -> GeneralResolution {
let indice = (self.shape_indice << 2) + self.size_indice;
let size_raw = INDICE_TO_SIZE_MAP[indice as usize].unwrap(); GeneralResolution::new(size_raw.0.into(), size_raw.1.into())
}
pub fn chunk_to_allocate_for_fragment(&self) -> u16 {
let size = self.size();
let blocks = (size.x * size.y / 256) as u16;
if blocks >= 1 {
blocks
} else {
1
}
}
pub fn find_smallest_containing(target_resolution: GeneralResolution) -> Option<OamShape> {
let mut optimal_result: Option<(u16, u16, OamShape)> = None; for (indice_compressed, entry) in INDICE_TO_SIZE_MAP.iter().copied().enumerate() {
if let Some(entry) = entry {
let resolution_entry = GeneralResolution::new(entry.0.into(), entry.1.into());
if resolution_entry.can_contain(target_resolution.clone()) {
let entry_oam = OamShape {
shape_indice: (indice_compressed as u8) >> 2,
size_indice: (indice_compressed as u8) & 0b11,
};
let chunk_to_allocate_entry = entry_oam.chunk_to_allocate_for_fragment();
let pixel_nb_entry = (resolution_entry.x as u16) * (resolution_entry.y as u16);
if let Some((chunk_to_allocate_optimal, pixel_nb_optimal, _)) = &optimal_result
{
if *chunk_to_allocate_optimal > chunk_to_allocate_entry
|| (*chunk_to_allocate_optimal == chunk_to_allocate_entry
&& *pixel_nb_optimal > pixel_nb_entry)
{
optimal_result =
Some((chunk_to_allocate_entry, pixel_nb_entry, entry_oam));
}
} else {
optimal_result = Some((chunk_to_allocate_entry, pixel_nb_entry, entry_oam));
};
}
}
}
optimal_result.map(|x| x.2)
}
}
#[cfg(test)]
mod tests {
use crate::{GeneralResolution, OamShape};
#[test]
fn test_resolution_chunk_allocation() {
for ((shape, size), expected_output) in [((0, 2), 4), ((1, 1), 1), ((0, 3), 16)].into_iter()
{
let resolution = OamShape::new(shape, size).unwrap();
let got = resolution.chunk_to_allocate_for_fragment();
if got != expected_output {
panic!(
"The resolution {:?} return the allocation number {}, but {} were expected",
resolution, got, expected_output
);
}
}
}
#[test]
pub fn test_creation_fail() {
assert_eq!(OamShape::new(2, 4), None);
assert_eq!(OamShape::new(3, 2), None);
assert_eq!(OamShape::new(12, 255), None);
}
#[test]
pub fn test_size() {
assert_eq!(
OamShape::new(0, 3).unwrap().size(),
GeneralResolution::new(64, 64)
);
assert_eq!(
OamShape::new(1, 2).unwrap().size(),
GeneralResolution::new(32, 16)
);
assert_eq!(
OamShape::new(1, 1).unwrap().size(),
GeneralResolution::new(32, 8)
);
assert_eq!(
OamShape::new(2, 3).unwrap().size(),
GeneralResolution::new(32, 64)
);
}
#[test]
pub fn test_find_smaller() {
assert_eq!(
OamShape::find_smallest_containing(GeneralResolution::new(6, 3)).unwrap(),
OamShape::new(0, 0).unwrap() );
assert_eq!(
OamShape::find_smallest_containing(GeneralResolution::new(64, 10)).unwrap(),
OamShape::new(1, 3).unwrap() );
let _ = OamShape::find_smallest_containing(GeneralResolution::new(0, 10));
assert_eq!(
OamShape::find_smallest_containing(GeneralResolution::new(90, 10)),
None
);
}
}