start on writing docs. A long way to go...

This commit is contained in:
Corwin 2023-04-05 19:29:29 +01:00
parent c84e81299b
commit c608458247
No known key found for this signature in database
8 changed files with 77 additions and 26 deletions

View file

@ -16,7 +16,6 @@ pub mod bitmap3;
pub mod bitmap4; pub mod bitmap4;
/// Test logo of agb. /// Test logo of agb.
pub mod example_logo; pub mod example_logo;
/// Implements sprites.
pub mod object; pub mod object;
/// Palette type. /// Palette type.
pub mod palette16; pub mod palette16;

View file

@ -1,10 +1,21 @@
#![warn(missing_docs)]
//! # Sprites and objects
//!
//! There are two implementations of objects depending on how you want to make
//! your game. There is the *Managed* and *Unmanaged* systems, given by
//! [OamManaged] and [OamUnmanaged] respectively. The managed Oam is easier to
//! use and has built in support for setting the `z` coordinate. The unmanaged
//! Oam is simpler and more efficient with the tradeoff that it is slightly
//! harder to integrate into your games depending on how they are architectured.
mod affine; mod affine;
mod managed; mod managed;
mod sprites; mod sprites;
mod unmanaged; mod unmanaged;
pub use sprites::{ pub use sprites::{
include_aseprite, DynamicSprite, Graphics, Size, Sprite, SpriteLoader, SpriteVram, Tag, TagMap, include_aseprite, DynamicSprite, Graphics, PaletteVram, Size, Sprite, SpriteLoader, SpriteVram,
Tag, TagMap,
}; };
pub use affine::AffineMatrixInstance; pub use affine::AffineMatrixInstance;

View file

@ -14,6 +14,11 @@ struct AffineMatrixData {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct AffineMatrixVram(Rc<AffineMatrixData>); pub(crate) struct AffineMatrixVram(Rc<AffineMatrixData>);
/// An affine matrix that can be used on objects. It is just in time copied to
/// vram, so you can have as many as you like of these but you can only use up
/// to 16 in one frame. They are reference counted (Cloning is cheap) and
/// immutable, if you want to change a matrix you must make a new one and set it
/// on all your objects.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AffineMatrixInstance { pub struct AffineMatrixInstance {
location: AffineMatrixVram, location: AffineMatrixVram,
@ -21,6 +26,9 @@ pub struct AffineMatrixInstance {
impl AffineMatrixInstance { impl AffineMatrixInstance {
#[must_use] #[must_use]
/// Creates an instance of an affine matrix from its object form. Check out
/// the docs for [AffineMatrix][crate::display::affine::AffineMatrix] to see
/// how you can use them to create effects.
pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrixInstance { pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrixInstance {
AffineMatrixInstance { AffineMatrixInstance {
location: AffineMatrixVram(Rc::new(AffineMatrixData { location: AffineMatrixVram(Rc::new(AffineMatrixData {

View file

@ -4,4 +4,4 @@ mod sprite_allocator;
const BYTES_PER_TILE_4BPP: usize = 32; const BYTES_PER_TILE_4BPP: usize = 32;
pub use sprite::{include_aseprite, Graphics, Size, Sprite, Tag, TagMap}; pub use sprite::{include_aseprite, Graphics, Size, Sprite, Tag, TagMap};
pub use sprite_allocator::{DynamicSprite, SpriteLoader, SpriteVram}; pub use sprite_allocator::{DynamicSprite, PaletteVram, SpriteLoader, SpriteVram};

View file

@ -28,6 +28,7 @@ impl Sprite {
} }
#[must_use] #[must_use]
/// Gives the size of the sprite
pub fn size(&self) -> Size { pub fn size(&self) -> Size {
self.size self.size
} }

View file

@ -87,14 +87,17 @@ impl Drop for PaletteVramData {
} }
} }
#[derive(Debug)] /// A palette in vram, this is reference counted so it is cheap to Clone.
#[derive(Debug, Clone)]
pub struct PaletteVram { pub struct PaletteVram {
data: Rc<PaletteVramData>, data: Rc<PaletteVramData>,
} }
impl PaletteVram { impl PaletteVram {
fn new(palette: &Palette16) -> Option<PaletteVram> { /// Attempts to allocate a new palette in sprite vram
let allocated = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout()) }?; pub fn new(palette: &Palette16) -> Result<PaletteVram, LoaderError> {
let allocated = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout()) }
.ok_or(LoaderError::PaletteFull)?;
unsafe { unsafe {
allocated allocated
@ -103,7 +106,7 @@ impl PaletteVram {
.copy_from_nonoverlapping(palette.colours.as_ptr(), palette.colours.len()); .copy_from_nonoverlapping(palette.colours.as_ptr(), palette.colours.len());
} }
Some(PaletteVram { Ok(PaletteVram {
data: Rc::new(PaletteVramData { data: Rc::new(PaletteVramData {
location: Location::from_palette_ptr(allocated), location: Location::from_palette_ptr(allocated),
}), }),
@ -124,20 +127,37 @@ impl Drop for SpriteVramData {
} }
} }
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LoaderError {
SpriteFull,
PaletteFull,
}
/// A sprite that is currently loaded into vram.
///
/// This is referenced counted such that clones of this are cheap and can be
/// reused between objects. When nothing references the sprite it gets
/// deallocated from vram.
///
/// You can create one of these either via the [DynamicSprite] interface, which
/// allows you to generate sprites at run time, or via a [SpriteLoader] (or
/// [OamManaged][super::super::OamManaged]).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SpriteVram { pub struct SpriteVram {
data: Rc<SpriteVramData>, data: Rc<SpriteVramData>,
} }
impl SpriteVram { impl SpriteVram {
fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option<SpriteVram> { fn new(data: &[u8], size: Size, palette: PaletteVram) -> Result<SpriteVram, LoaderError> {
let allocated = unsafe { SPRITE_ALLOCATOR.alloc(size.layout()) }?; let allocated =
unsafe { SPRITE_ALLOCATOR.alloc(size.layout()) }.ok_or(LoaderError::SpriteFull)?;
unsafe { unsafe {
allocated allocated
.as_ptr() .as_ptr()
.copy_from_nonoverlapping(data.as_ptr(), data.len()); .copy_from_nonoverlapping(data.as_ptr(), data.len());
} }
Some(SpriteVram { Ok(SpriteVram {
data: Rc::new(SpriteVramData { data: Rc::new(SpriteVramData {
location: Location::from_sprite_ptr(allocated), location: Location::from_sprite_ptr(allocated),
size, size,
@ -163,19 +183,19 @@ impl SpriteLoader {
fn create_sprite_no_insert( fn create_sprite_no_insert(
palette_map: &mut HashMap<PaletteId, Weak<PaletteVramData>>, palette_map: &mut HashMap<PaletteId, Weak<PaletteVramData>>,
sprite: &'static Sprite, sprite: &'static Sprite,
) -> Option<(Weak<SpriteVramData>, SpriteVram)> { ) -> Result<(Weak<SpriteVramData>, SpriteVram), LoaderError> {
let palette = Self::try_get_vram_palette_asoc(palette_map, sprite.palette)?; let palette = Self::try_get_vram_palette_asoc(palette_map, sprite.palette)?;
let sprite = SpriteVram::new(sprite.data, sprite.size, palette)?; let sprite = SpriteVram::new(sprite.data, sprite.size, palette)?;
Some((Rc::downgrade(&sprite.data), sprite)) Ok((Rc::downgrade(&sprite.data), sprite))
} }
fn try_get_vram_palette_asoc( fn try_get_vram_palette_asoc(
palette_map: &mut HashMap<PaletteId, Weak<PaletteVramData>>, palette_map: &mut HashMap<PaletteId, Weak<PaletteVramData>>,
palette: &'static Palette16, palette: &'static Palette16,
) -> Option<PaletteVram> { ) -> Result<PaletteVram, LoaderError> {
let id = PaletteId::from_static_palette(palette); let id = PaletteId::from_static_palette(palette);
Some(match palette_map.entry(id) { Ok(match palette_map.entry(id) {
crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() {
Some(data) => PaletteVram { data }, Some(data) => PaletteVram { data },
None => { None => {
@ -192,12 +212,16 @@ impl SpriteLoader {
}) })
} }
pub fn try_get_vram_sprite(&mut self, sprite: &'static Sprite) -> Option<SpriteVram> { /// Attempts to get a sprite
pub fn try_get_vram_sprite(
&mut self,
sprite: &'static Sprite,
) -> Result<SpriteVram, LoaderError> {
// check if we already have the sprite in vram // check if we already have the sprite in vram
let id = SpriteId::from_static_sprite(sprite); let id = SpriteId::from_static_sprite(sprite);
Some(match self.static_sprite_map.entry(id) { Ok(match self.static_sprite_map.entry(id) {
crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() {
Some(data) => SpriteVram { data }, Some(data) => SpriteVram { data },
None => { None => {
@ -216,18 +240,24 @@ impl SpriteLoader {
}) })
} }
pub fn try_get_vram_palette(&mut self, palette: &'static Palette16) -> Option<PaletteVram> { /// Attempts to allocate a static palette
pub fn try_get_vram_palette(
&mut self,
palette: &'static Palette16,
) -> Result<PaletteVram, LoaderError> {
Self::try_get_vram_palette_asoc(&mut self.static_palette_map, palette) Self::try_get_vram_palette_asoc(&mut self.static_palette_map, palette)
} }
/// Allocates a sprite to vram, panics if it cannot fit.
pub fn get_vram_sprite(&mut self, sprite: &'static Sprite) -> SpriteVram { pub fn get_vram_sprite(&mut self, sprite: &'static Sprite) -> SpriteVram {
self.try_get_vram_sprite(sprite) self.try_get_vram_sprite(sprite)
.expect("no free sprite slots") .expect("cannot create sprite")
} }
/// Allocates a palette to vram, panics if it cannot fit.
pub fn get_vram_palette(&mut self, palette: &'static Palette16) -> PaletteVram { pub fn get_vram_palette(&mut self, palette: &'static Palette16) -> PaletteVram {
self.try_get_vram_palette(palette) self.try_get_vram_palette(palette)
.expect("no free palette slots") .expect("cannot create sprite")
} }
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
@ -237,6 +267,9 @@ impl SpriteLoader {
} }
} }
/// Remove internal references to sprites that no longer exist in vram. If
/// you neglect calling this, memory will leak over time in relation to the
/// total number of different sprites used. It will not leak vram.
pub fn garbage_collect(&mut self) { pub fn garbage_collect(&mut self) {
self.static_sprite_map self.static_sprite_map
.retain(|_, v| Weak::strong_count(v) != 0); .retain(|_, v| Weak::strong_count(v) != 0);
@ -277,18 +310,15 @@ impl DynamicSprite<'_> {
DynamicSprite { data, size } DynamicSprite { data, size }
} }
#[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.
/// Returns None if there is no room in sprite vram. pub fn try_vram(&self, palette: PaletteVram) -> Result<SpriteVram, LoaderError> {
pub fn try_vram(&self, palette: PaletteVram) -> Option<SpriteVram> {
SpriteVram::new(self.data, self.size, palette) 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 there is no room in sprite vram. /// 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) self.try_vram(palette).expect("cannot create sprite")
.expect("No slot for sprite available")
} }
} }

View file

@ -26,8 +26,11 @@ impl Default for Attributes {
} }
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// The affine mode
pub enum AffineMode { pub enum AffineMode {
/// Normal affine, this is where the area of the affine is equal to the sprite size
Affine = 1, Affine = 1,
/// Double affine, this is where the area of the affine is double that of the sprite
AffineDouble = 3, AffineDouble = 3,
} }

View file

@ -14,7 +14,6 @@ impl Palette16 {
// Clippy bug: claims that index is only used in recursion. I can't reproduce in // Clippy bug: claims that index is only used in recursion. I can't reproduce in
// other examples, even just copy pasting this struct and impl into a blank project :/ // other examples, even just copy pasting this struct and impl into a blank project :/
#[allow(clippy::only_used_in_recursion)]
pub fn update_colour(&mut self, index: usize, colour: u16) { pub fn update_colour(&mut self, index: usize, colour: u16) {
self.colours[index] = colour; self.colours[index] = colour;
} }