From fd82b259ccc49ab0f669971fe7328cdae46331b6 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 27 Jun 2023 21:56:09 +0100 Subject: [PATCH] dynamic sprite allocate directly into sprite vram --- agb/src/agb_alloc/mod.rs | 12 +- .../object/sprites/sprite_allocator.rs | 119 ++++++++++++------ agb/src/no_game.rs | 9 +- 3 files changed, 90 insertions(+), 50 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 8968fd5f..7fae40fe 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -1,4 +1,3 @@ -use core::alloc::{Allocator, Layout}; use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; @@ -45,18 +44,23 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe { macro_rules! impl_zst_allocator { ($name_of_struct: ty, $name_of_static: ident) => { - unsafe impl Allocator for $name_of_struct { - fn allocate(&self, layout: Layout) -> Result, core::alloc::AllocError> { + unsafe impl core::alloc::Allocator for $name_of_struct { + fn allocate( + &self, + layout: core::alloc::Layout, + ) -> Result, core::alloc::AllocError> { $name_of_static.allocate(layout) } - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe fn deallocate(&self, ptr: core::ptr::NonNull, layout: core::alloc::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 /// 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 diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index 202860a0..8050347b 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -1,14 +1,12 @@ use core::{alloc::Allocator, ptr::NonNull}; use alloc::{ - alloc::Global, boxed::Box, rc::{Rc, Weak}, - vec::Vec, }; use crate::{ - agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd}, + agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd, impl_zst_allocator}, display::palette16::Palette16, hash_map::HashMap, }; @@ -18,8 +16,8 @@ use super::{ BYTES_PER_TILE_4BPP, }; -const PALETTE_SPRITE: usize = 0x0500_0200; -const TILE_SPRITE: usize = 0x06010000; +pub const PALETTE_SPRITE: usize = 0x0500_0200; +pub const TILE_SPRITE: usize = 0x06010000; static SPRITE_ALLOCATOR: BlockAllocator = unsafe { 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 { BlockAllocator::new(StartEnd { 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 /// rom and is therefore a unique identifier to a sprite #[derive(Clone, Copy, PartialEq, Eq, Hash)] @@ -162,13 +168,21 @@ impl SpriteVram { .as_ptr() .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, + size: Size, + palette: PaletteVram, + ) -> SpriteVram { + SpriteVram { data: Rc::new(SpriteVramData { - location: Location::from_sprite_ptr(allocated), + location: Location::from_sprite_ptr(data), size, palette, }), - }) + } } 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. -pub struct DynamicSprite { - data: Box<[u8], A>, +pub struct DynamicSprite { + data: Box<[u16], SpriteAllocator>, size: Size, } -impl DynamicSprite { - #[must_use] - /// Creates a new dynamic sprite. - pub fn new(size: Size) -> Self { - Self::new_in(size, Global) +impl Clone for DynamicSprite { + fn clone(&self) -> Self { + let allocation = SpriteAllocator + .allocate(self.size.layout()) + .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 DynamicSprite { +impl DynamicSprite { + /// Creates a new dynamic sprite of a given size + pub fn try_new(size: Size) -> Result { + 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] - /// Creates a new dynamic sprite of a given size in a given allocator. - pub fn new_in(size: Size, allocator: A) -> Self { - let num_bytes = size.number_of_tiles() * BYTES_PER_TILE_4BPP; - let mut data = Vec::with_capacity_in(num_bytes, allocator); - - data.resize(num_bytes, 0); - - let data = data.into_boxed_slice(); - - DynamicSprite { data, size } + /// Creates a new dynamic sprite of a given size + pub fn new(size: Size) -> Self { + Self::try_new(size).expect("couldn't allocate dynamic sprite") } /// Set the pixel of a sprite to a given paletted pixel. Panics if the @@ -335,32 +372,34 @@ impl DynamicSprite { 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 mut byte = self.data[byte_to_modify]; - let parity = (x & 0b1) * 4; + let half_word_to_modify = + tile_number_to_modify * BYTES_PER_TILE_4BPP / 2 + half_word_to_modify_in_tile; + let mut half_word = self.data[half_word_to_modify]; - byte = (byte & !(0b1111 << parity)) | ((paletted_pixel as u8) << parity); - self.data[byte_to_modify] = byte; + let nibble_to_modify = (x % 4) * 4; + + 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 pub fn clear(&mut self, paletted_pixel: usize) { 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); } - /// Tries to copy the sprite to vram to be used to set object sprites. - pub fn try_vram(&self, palette: PaletteVram) -> Result { - SpriteVram::new(&self.data, self.size, palette) - } - #[must_use] /// Tries to copy the sprite to vram to be used to set object sprites. /// Panics if it cannot be allocated. - pub fn to_vram(&self, palette: PaletteVram) -> SpriteVram { - self.try_vram(palette).expect("cannot create sprite") + pub fn to_vram(self, palette: PaletteVram) -> SpriteVram { + let data = unsafe { NonNull::new_unchecked(Box::leak(self.data).as_mut_ptr()) }; + + unsafe { SpriteVram::from_location_size(data.cast(), self.size, palette) } } } diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index ed82f880..0b84e5f1 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -129,13 +129,10 @@ fn generate_sprites() -> Box<[SpriteVram]> { .collect(); // generate sprites - let mut sprite = DynamicSprite::new(Size::S8x8); + for (palette, colour) in (0..PALETTE.len()).map(|x| (x / 15, x % 15)) { - for y in 0..8 { - for x in 0..8 { - sprite.set_pixel(x, y, colour + 1); - } - } + let mut sprite = DynamicSprite::new(Size::S8x8); + sprite.clear(colour + 1); sprites.push(sprite.to_vram(palettes[palette].clone())); }