mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
dynamic sprite allocate directly into sprite vram
This commit is contained in:
parent
ce7bcacb3c
commit
fd82b259cc
|
@ -1,4 +1,3 @@
|
||||||
use core::alloc::{Allocator, Layout};
|
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
@ -45,18 +44,23 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe {
|
||||||
|
|
||||||
macro_rules! impl_zst_allocator {
|
macro_rules! impl_zst_allocator {
|
||||||
($name_of_struct: ty, $name_of_static: ident) => {
|
($name_of_struct: ty, $name_of_static: ident) => {
|
||||||
unsafe impl Allocator for $name_of_struct {
|
unsafe impl core::alloc::Allocator for $name_of_struct {
|
||||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, core::alloc::AllocError> {
|
fn allocate(
|
||||||
|
&self,
|
||||||
|
layout: core::alloc::Layout,
|
||||||
|
) -> Result<core::ptr::NonNull<[u8]>, core::alloc::AllocError> {
|
||||||
$name_of_static.allocate(layout)
|
$name_of_static.allocate(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: core::alloc::Layout) {
|
||||||
$name_of_static.deallocate(ptr, layout)
|
$name_of_static.deallocate(ptr, layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) use impl_zst_allocator;
|
||||||
|
|
||||||
/// This is the allocator for the External Working Ram. This is currently
|
/// This is the allocator for the External Working Ram. This is currently
|
||||||
/// equivalent to the Global Allocator (where things are allocated if no allocator is provided). This implements the allocator trait, so
|
/// equivalent to the Global Allocator (where things are allocated if no allocator is provided). This implements the allocator trait, so
|
||||||
/// is meant to be used in specifying where certain structures should be
|
/// is meant to be used in specifying where certain structures should be
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use core::{alloc::Allocator, ptr::NonNull};
|
use core::{alloc::Allocator, ptr::NonNull};
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
alloc::Global,
|
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd},
|
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd, impl_zst_allocator},
|
||||||
display::palette16::Palette16,
|
display::palette16::Palette16,
|
||||||
hash_map::HashMap,
|
hash_map::HashMap,
|
||||||
};
|
};
|
||||||
|
@ -18,8 +16,8 @@ use super::{
|
||||||
BYTES_PER_TILE_4BPP,
|
BYTES_PER_TILE_4BPP,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PALETTE_SPRITE: usize = 0x0500_0200;
|
pub const PALETTE_SPRITE: usize = 0x0500_0200;
|
||||||
const TILE_SPRITE: usize = 0x06010000;
|
pub const TILE_SPRITE: usize = 0x06010000;
|
||||||
|
|
||||||
static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
|
static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
BlockAllocator::new(StartEnd {
|
BlockAllocator::new(StartEnd {
|
||||||
|
@ -28,6 +26,10 @@ static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct SpriteAllocator;
|
||||||
|
|
||||||
|
impl_zst_allocator!(SpriteAllocator, SPRITE_ALLOCATOR);
|
||||||
|
|
||||||
static PALETTE_ALLOCATOR: BlockAllocator = unsafe {
|
static PALETTE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
BlockAllocator::new(StartEnd {
|
BlockAllocator::new(StartEnd {
|
||||||
start: || PALETTE_SPRITE,
|
start: || PALETTE_SPRITE,
|
||||||
|
@ -35,6 +37,10 @@ static PALETTE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct PaletteAllocator;
|
||||||
|
|
||||||
|
impl_zst_allocator!(PaletteAllocator, PALETTE_ALLOCATOR);
|
||||||
|
|
||||||
/// The Sprite Id is a thin wrapper around the pointer to the sprite in
|
/// The Sprite Id is a thin wrapper around the pointer to the sprite in
|
||||||
/// rom and is therefore a unique identifier to a sprite
|
/// rom and is therefore a unique identifier to a sprite
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -162,13 +168,21 @@ impl SpriteVram {
|
||||||
.as_ptr()
|
.as_ptr()
|
||||||
.copy_from_nonoverlapping(data.as_ptr(), data.len());
|
.copy_from_nonoverlapping(data.as_ptr(), data.len());
|
||||||
}
|
}
|
||||||
Ok(SpriteVram {
|
Ok(unsafe { Self::from_location_size(allocated, size, palette) })
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_location_size(
|
||||||
|
data: NonNull<u8>,
|
||||||
|
size: Size,
|
||||||
|
palette: PaletteVram,
|
||||||
|
) -> SpriteVram {
|
||||||
|
SpriteVram {
|
||||||
data: Rc::new(SpriteVramData {
|
data: Rc::new(SpriteVramData {
|
||||||
location: Location::from_sprite_ptr(allocated),
|
location: Location::from_sprite_ptr(data),
|
||||||
size,
|
size,
|
||||||
palette,
|
palette,
|
||||||
}),
|
}),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn location(&self) -> u16 {
|
pub(crate) fn location(&self) -> u16 {
|
||||||
|
@ -290,31 +304,54 @@ impl Default for SpriteLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sprite data that can be used to create sprites in vram.
|
/// Sprite data that can be used to create sprites in vram.
|
||||||
pub struct DynamicSprite<A: Allocator = Global> {
|
pub struct DynamicSprite {
|
||||||
data: Box<[u8], A>,
|
data: Box<[u16], SpriteAllocator>,
|
||||||
size: Size,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicSprite {
|
impl Clone for DynamicSprite {
|
||||||
#[must_use]
|
fn clone(&self) -> Self {
|
||||||
/// Creates a new dynamic sprite.
|
let allocation = SpriteAllocator
|
||||||
pub fn new(size: Size) -> Self {
|
.allocate(self.size.layout())
|
||||||
Self::new_in(size, Global)
|
.expect("cannot allocate dynamic sprite");
|
||||||
|
|
||||||
|
let allocation = core::ptr::slice_from_raw_parts_mut(
|
||||||
|
allocation.as_ptr() as *mut _,
|
||||||
|
allocation.len() / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut data = unsafe { Box::from_raw_in(allocation, SpriteAllocator) };
|
||||||
|
|
||||||
|
data.clone_from_slice(&self.data);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
data,
|
||||||
|
size: self.size,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Allocator> DynamicSprite<A> {
|
impl DynamicSprite {
|
||||||
|
/// Creates a new dynamic sprite of a given size
|
||||||
|
pub fn try_new(size: Size) -> Result<Self, LoaderError> {
|
||||||
|
let allocation = SpriteAllocator
|
||||||
|
.allocate_zeroed(size.layout())
|
||||||
|
.map_err(|_| LoaderError::SpriteFull)?;
|
||||||
|
|
||||||
|
let allocation = core::ptr::slice_from_raw_parts_mut(
|
||||||
|
allocation.as_ptr() as *mut _,
|
||||||
|
allocation.len() / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = unsafe { Box::from_raw_in(allocation, SpriteAllocator) };
|
||||||
|
|
||||||
|
Ok(DynamicSprite { data, size })
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Creates a new dynamic sprite of a given size in a given allocator.
|
/// Creates a new dynamic sprite of a given size
|
||||||
pub fn new_in(size: Size, allocator: A) -> Self {
|
pub fn new(size: Size) -> Self {
|
||||||
let num_bytes = size.number_of_tiles() * BYTES_PER_TILE_4BPP;
|
Self::try_new(size).expect("couldn't allocate dynamic sprite")
|
||||||
let mut data = Vec::with_capacity_in(num_bytes, allocator);
|
|
||||||
|
|
||||||
data.resize(num_bytes, 0);
|
|
||||||
|
|
||||||
let data = data.into_boxed_slice();
|
|
||||||
|
|
||||||
DynamicSprite { data, size }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the pixel of a sprite to a given paletted pixel. Panics if the
|
/// Set the pixel of a sprite to a given paletted pixel. Panics if the
|
||||||
|
@ -335,32 +372,34 @@ impl<A: Allocator> DynamicSprite<A> {
|
||||||
|
|
||||||
let (x_in_tile, y_in_tile) = (x % 8, y % 8);
|
let (x_in_tile, y_in_tile) = (x % 8, y % 8);
|
||||||
|
|
||||||
let byte_to_modify_in_tile = x_in_tile / 2 + y_in_tile * 4;
|
let half_word_to_modify_in_tile = x_in_tile / 4 + y_in_tile * 2;
|
||||||
|
|
||||||
let byte_to_modify = tile_number_to_modify * BYTES_PER_TILE_4BPP + byte_to_modify_in_tile;
|
let half_word_to_modify =
|
||||||
let mut byte = self.data[byte_to_modify];
|
tile_number_to_modify * BYTES_PER_TILE_4BPP / 2 + half_word_to_modify_in_tile;
|
||||||
let parity = (x & 0b1) * 4;
|
let mut half_word = self.data[half_word_to_modify];
|
||||||
|
|
||||||
byte = (byte & !(0b1111 << parity)) | ((paletted_pixel as u8) << parity);
|
let nibble_to_modify = (x % 4) * 4;
|
||||||
self.data[byte_to_modify] = byte;
|
|
||||||
|
half_word = (half_word & !(0b1111 << nibble_to_modify))
|
||||||
|
| ((paletted_pixel as u16) << nibble_to_modify);
|
||||||
|
self.data[half_word_to_modify] = half_word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wipes the sprite
|
/// Wipes the sprite
|
||||||
pub fn clear(&mut self, paletted_pixel: usize) {
|
pub fn clear(&mut self, paletted_pixel: usize) {
|
||||||
assert!(paletted_pixel < 0x10);
|
assert!(paletted_pixel < 0x10);
|
||||||
let reset = (paletted_pixel | paletted_pixel << 4) as u8;
|
let reset =
|
||||||
|
(paletted_pixel | paletted_pixel << 4 | paletted_pixel << 8 | paletted_pixel << 12)
|
||||||
|
as u16;
|
||||||
self.data.fill(reset);
|
self.data.fill(reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to copy the sprite to vram to be used to set object sprites.
|
|
||||||
pub fn try_vram(&self, palette: PaletteVram) -> Result<SpriteVram, LoaderError> {
|
|
||||||
SpriteVram::new(&self.data, self.size, palette)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Tries to copy the sprite to vram to be used to set object sprites.
|
/// Tries to copy the sprite to vram to be used to set object sprites.
|
||||||
/// Panics if it cannot be allocated.
|
/// Panics if it cannot be allocated.
|
||||||
pub fn to_vram(&self, palette: PaletteVram) -> SpriteVram {
|
pub fn to_vram(self, palette: PaletteVram) -> SpriteVram {
|
||||||
self.try_vram(palette).expect("cannot create sprite")
|
let data = unsafe { NonNull::new_unchecked(Box::leak(self.data).as_mut_ptr()) };
|
||||||
|
|
||||||
|
unsafe { SpriteVram::from_location_size(data.cast(), self.size, palette) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,13 +129,10 @@ fn generate_sprites() -> Box<[SpriteVram]> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// generate sprites
|
// generate sprites
|
||||||
let mut sprite = DynamicSprite::new(Size::S8x8);
|
|
||||||
for (palette, colour) in (0..PALETTE.len()).map(|x| (x / 15, x % 15)) {
|
for (palette, colour) in (0..PALETTE.len()).map(|x| (x / 15, x % 15)) {
|
||||||
for y in 0..8 {
|
let mut sprite = DynamicSprite::new(Size::S8x8);
|
||||||
for x in 0..8 {
|
sprite.clear(colour + 1);
|
||||||
sprite.set_pixel(x, y, colour + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sprites.push(sprite.to_vram(palettes[palette].clone()));
|
sprites.push(sprite.to_vram(palettes[palette].clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue