Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplified slot set #537

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agb/src/display/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub use sprites::{
};

pub use affine::AffineMatrixInstance;
pub use managed::{OamManaged, Object};
pub use managed::{OamManaged, Object, OrderedStore, OrderedStoreIterator};
pub use unmanaged::{AffineMode, OamIterator, OamSlot, OamUnmanaged, ObjectUnmanaged};

pub use font::{ChangeColour, ObjectTextRender, TextAlignment};
Expand Down
88 changes: 75 additions & 13 deletions agb/src/display/object/managed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,85 @@ impl Store {
///
/// Otherwise I'd recommend using [`OamUnmanaged`].
pub struct OamManaged<'gba> {
object_store: Store,
object_store: OrderedStore,
sprite_loader: UnsafeCell<SpriteLoader>,
unmanaged: UnsafeCell<OamUnmanaged<'gba>>,
}

impl OamManaged<'_> {
pub(crate) fn new() -> Self {
/// Stores a bunch of objects and manages the z ordering for you.
///
/// An alternate to consider is using an arena, storing keys in a vector, and
/// sorting that vector by key in the arena.
pub struct OrderedStore {
object_store: Store,
}

/// An iterator over the visible objects in the object store.
pub struct OrderedStoreIterator<'store> {
iter: StoreIterator<'store>,
}

impl<'store> Iterator for OrderedStoreIterator<'store> {
type Item = &'store ObjectUnmanaged;

fn next(&mut self) -> Option<Self::Item> {
for next in self.iter.by_ref() {
let item = unsafe { &*next.object.get() };
if item.is_visible() {
return Some(item);
}
}

None
}
}

impl<'a> IntoIterator for &'a OrderedStore {
type Item = &'a ObjectUnmanaged;

type IntoIter = OrderedStoreIterator<'a>;

fn into_iter(self) -> Self::IntoIter {
OrderedStoreIterator {
iter: unsafe { self.object_store.iter() },
}
}
}

impl OrderedStore {
/// Creates a new empty ordered object store.
#[must_use]
pub fn new() -> Self {
Self {
object_store: Store {
store: UnsafeCell::new(Arena::new()),
first_z: Cell::new(None),
},
}
}

/// Creates an object from the sprite in vram.
pub fn object(&self, sprite: SpriteVram) -> Object<'_> {
self.object_store
.insert_object(ObjectUnmanaged::new(sprite))
}

/// Iter over the ordered store in order
pub fn iter(&self) -> OrderedStoreIterator {
self.into_iter()
}
}

impl Default for OrderedStore {
fn default() -> Self {
Self::new()
}
}

impl OamManaged<'_> {
pub(crate) fn new() -> Self {
Self {
object_store: OrderedStore::new(),
sprite_loader: UnsafeCell::new(SpriteLoader::new()),
unmanaged: UnsafeCell::new(OamUnmanaged::new()),
}
Expand All @@ -169,13 +236,9 @@ impl OamManaged<'_> {
// safety: commit is not reentrant
let unmanaged = unsafe { &mut *self.unmanaged.get() };

for (object, slot) in unsafe { self.object_store.iter() }
.map(|item| unsafe { &*item.object.get() })
.filter(|object| object.is_visible())
.zip(unmanaged.iter())
{
slot.set(object);
}
let mut unmanaged = unmanaged.iter();

unmanaged.set(&self.object_store);

// safety: not reentrant
unsafe {
Expand All @@ -185,8 +248,7 @@ impl OamManaged<'_> {

/// Creates an object from the sprite in vram.
pub fn object(&self, sprite: SpriteVram) -> Object<'_> {
self.object_store
.insert_object(ObjectUnmanaged::new(sprite))
self.object_store.object(sprite)
}

/// Creates a sprite in vram from a static sprite from [`include_aseprite`][crate::include_aseprite].
Expand Down Expand Up @@ -500,7 +562,7 @@ mod tests {
objects[index_to_modify].set_z(modify_to);

assert!(
managed.object_store.is_all_ordered_right(),
managed.object_store.object_store.is_all_ordered_right(),
"objects are unordered after {} modifications. Modified {} to {}.",
modification_number + 1,
index_to_modify,
Expand Down
63 changes: 63 additions & 0 deletions agb/src/display/object/unmanaged/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ pub struct OamIterator<'oam> {
frame_data: &'oam UnsafeCell<OamFrameModifyables>,
}

impl OamIterator<'_> {
fn set_inner(&mut self, object: &ObjectUnmanaged) -> OamDisplayResult {
if let Some(slot) = self.next() {
slot.set(object);
OamDisplayResult::Written
} else {
OamDisplayResult::SomeNotWritten
}
}

/// Writes objects in the Renderable to slots in OAM.
pub fn set<R: OamDisplay>(&mut self, renderable: R) {
renderable.set_in(self);
}
}

/// A slot in Oam that you can write to. Note that you must call [OamSlot::set]
/// or else it is a bug and will panic when dropped.
///
Expand Down Expand Up @@ -337,6 +353,53 @@ impl ObjectUnmanaged {
}
}

pub trait OamDisplay {
/// Write it to oam slots, returns whether all the writes could succeed.
fn set_in(self, oam: &mut OamIterator) -> OamDisplayResult;
}

impl OamDisplay for ObjectUnmanaged {
fn set_in(self, oam: &mut OamIterator) -> OamDisplayResult {
oam.set_inner(&self)
}
}

impl OamDisplay for &ObjectUnmanaged {
fn set_in(self, oam: &mut OamIterator) -> OamDisplayResult {
oam.set_inner(self)
}
}

impl OamDisplay for &mut ObjectUnmanaged {
fn set_in(self, oam: &mut OamIterator) -> OamDisplayResult {
oam.set_inner(self)
}
}

impl<T, O> OamDisplay for T
where
T: IntoIterator<Item = O>,
O: OamDisplay,
{
fn set_in(self, oam: &mut OamIterator) -> OamDisplayResult {
for object in self.into_iter() {
if matches!(object.set_in(oam), OamDisplayResult::SomeNotWritten) {
return OamDisplayResult::SomeNotWritten;
}
}

OamDisplayResult::Written
}
}

/// The result of setting on the Oam
pub enum OamDisplayResult {
/// All objects were written successfully
Written,
/// Some objects were not written
SomeNotWritten,
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down