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

Fix sprite memory leak #413

Merged
merged 2 commits into from
Aug 24, 2024
Merged
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
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ lua = { version = "0.1", path = "api/lua", package = "playdate-lua", default-fea
menu = { version = "0.2", path = "api/menu", package = "playdate-menu", default-features = false }
scoreboards = { version = "0.1", path = "api/scoreboards", package = "playdate-scoreboards", default-features = false }
sound = { version = "0.4", path = "api/sound", package = "playdate-sound", default-features = false }
sprite = { version = "0.2", path = "api/sprite", package = "playdate-sprite", default-features = false }
sprite = { version = "0.3", path = "api/sprite", package = "playdate-sprite", default-features = false }
system = { version = "0.3", path = "api/system", package = "playdate-system", default-features = false }
sys = { version = "0.4", path = "api/sys", package = "playdate-sys", default-features = false }

Expand Down
2 changes: 1 addition & 1 deletion api/playdate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate"
version = "0.2.2"
version = "0.2.3"
readme = "README.md"
description = "High-level Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
Expand Down
2 changes: 1 addition & 1 deletion api/sprite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate-sprite"
version = "0.2.11"
version = "0.3.0"
readme = "README.md"
description = "High-level sprite API built on-top of Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
Expand Down
43 changes: 43 additions & 0 deletions api/sprite/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(not(test), no_std)]
#![feature(const_alloc_layout)]

extern crate sys;
extern crate alloc;
Expand Down Expand Up @@ -293,3 +294,45 @@ impl<T: TypedSprite> SpriteType for T {
type Userdata = <T as TypedSprite>::Userdata;
const FREE_ON_DROP: bool = <T as TypedSprite>::FREE_ON_DROP;
}


pub mod utils {
use core::ops::Deref;

/// C array syzed at runtime.
#[must_use]
#[repr(transparent)]
pub struct Arr<'t, T>(pub(super) &'t [T]);

impl<T> Drop for Arr<'_, T> {
fn drop(&mut self) {
let p = self.0.as_ptr() as _;

#[inline]
const fn inner<T>(len: usize) -> core::alloc::Layout {
if let Ok(l) = core::alloc::Layout::array::<T>(len) {
l
} else {
use core::mem::{size_of, align_of};
let (size, align) = (size_of::<T>(), align_of::<T>());
unsafe { core::alloc::Layout::from_size_align_unchecked(size.unchecked_mul(len), align) }
}
}

let l = inner::<T>(self.0.len());
unsafe {
// We could simply `sys::allocator::dealloc(p)`, but we have to use SYSTEM GLOBAL allocator,
// which can be a user's custom allocator, not that one in `playdate-sys`.
alloc::alloc::dealloc(p, l);
};
}
}

impl<T> Deref for Arr<'_, T> {
type Target = [T];
fn deref(&self) -> &Self::Target { self.0 }
}
impl<T> AsRef<[T]> for Arr<'_, T> {
fn as_ref(&self) -> &[T] { self.0 }
}
}
40 changes: 28 additions & 12 deletions api/sprite/src/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
//! Sprite implementations.

/*
TODO: Cover api-methods:
- [] querySpritesInRect
- [] querySpritesAlongLine
- [] querySpriteInfoAlongLine
- [] overlappingSprites
- [] allOverlappingSprites
*/


use core::ffi::c_int;
use core::ffi::c_void;
use core::ffi::c_float;
Expand All @@ -18,6 +28,7 @@ use gfx::bitmap::BitmapRef;
use gfx::bitmap::BitmapDrawMode;
use gfx::bitmap::BitmapFlip;

use crate::utils;
use crate::AnySprite;
use crate::SpriteApi;
use crate::TypedSprite;
Expand Down Expand Up @@ -524,22 +535,22 @@ impl<Userdata, Api: api::Api, const FOD: bool> Sprite<Userdata, Api, FOD> {
///
/// Equivalent to [`sys::ffi::playdate_sprite::checkCollisions`]
#[doc(alias = "sys::ffi::playdate_sprite::check_collisions")]
#[must_use = "Result is borrowed by C-API"]
#[must_use = "Expensive op, allocated array by C-API"]
pub fn check_collisions(&self,
goal_x: c_float,
goal_y: c_float,
actual_x: &mut c_float,
actual_y: &mut c_float)
-> &[SpriteCollisionInfo] {
-> Option<utils::Arr<SpriteCollisionInfo>> {
let f = self.1.check_collisions();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };

if ptr.is_null() || len == 0 {
&[]
None
} else {
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
slice
Some(utils::Arr(slice))
}
}

Expand All @@ -553,22 +564,22 @@ impl<Userdata, Api: api::Api, const FOD: bool> Sprite<Userdata, Api, FOD> {
///
/// Equivalent to [`sys::ffi::playdate_sprite::moveWithCollisions`]
#[doc(alias = "sys::ffi::playdate_sprite::moveWithCollisions")]
#[must_use = "Result is borrowed by C-API"]
#[must_use = "Expensive op, allocated array by C-API"]
pub fn move_with_collisions<'t>(&'t self,
goal_x: c_float,
goal_y: c_float,
actual_x: &mut c_float,
actual_y: &mut c_float)
-> &'t [SpriteCollisionInfo] {
-> Option<utils::Arr<'t, SpriteCollisionInfo>> {
let f = self.1.move_with_collisions();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };

if ptr.is_null() || len == 0 {
&[]
None
} else {
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
slice
Some(utils::Arr(slice))
}
}

Expand All @@ -578,13 +589,18 @@ impl<Userdata, Api: api::Api, const FOD: bool> Sprite<Userdata, Api, FOD> {
///
/// Equivalent to [`sys::ffi::playdate_sprite::overlappingSprites`]
#[doc(alias = "sys::ffi::playdate_sprite::overlapping_sprites")]
#[must_use = "Result is borrowed by C-API"]
pub fn overlapping_sprites(&self) -> &[SpriteRef] {
#[must_use = "Expensive op, allocated array by C-API"]
pub fn overlapping_sprites(&self) -> Option<utils::Arr<SpriteRef>> {
let f = self.1.overlapping_sprites();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, &mut len) };
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
unsafe { core::mem::transmute(slice) }
if ptr.is_null() || len == 0 {
None
} else {
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
let res = unsafe { core::mem::transmute(slice) };
Some(utils::Arr(res))
}
}


Expand Down
2 changes: 1 addition & 1 deletion api/sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "playdate-sys"
version = "0.4.4"
version = "0.4.5"
build = "src/build.rs"
readme = "README.md"
description = "Low-level Playdate API bindings"
Expand Down
7 changes: 6 additions & 1 deletion api/sys/src/sys/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ unsafe impl GlobalAlloc for PlaydateAllocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { realloc(core::ptr::null_mut(), layout.size()) as *mut u8 }
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { realloc(ptr as *mut c_void, 0); }
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { dealloc(ptr as *mut c_void); }
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
realloc(ptr as *mut c_void, new_size) as *mut u8
Expand All @@ -56,3 +56,8 @@ unsafe fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void {
});
f(ptr, size)
}


#[track_caller]
#[inline(always)]
pub unsafe fn dealloc(ptr: *mut c_void) { realloc(ptr, 0); }
Loading