Skip to content
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
15 changes: 9 additions & 6 deletions examples/cube/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,21 @@ unsafe fn psp_main_inner() {
psp::enable_home_button();

let mut allocator = get_vram_allocator().unwrap();
let fbp0 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
let fbp1 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
let zbp = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm4444).as_mut_ptr_from_zero();
let fbp0 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888);
let fbp1 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888);
let zbp = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm4444);
// Attempting to free the three VRAM chunks at this point would give a
// compile-time error since fbp0, fbp1 and zbp are used later on
//allocator.free_all();

sys::sceGumLoadIdentity();

sys::sceGuInit();

sys::sceGuStart(GuContextType::Direct, &mut LIST.0 as *mut [u32; 0x40000] as *mut _);
sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, fbp0 as _, BUF_WIDTH as i32);
sys::sceGuDispBuffer(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32, fbp1 as _, BUF_WIDTH as i32);
sys::sceGuDepthBuffer(zbp as _, BUF_WIDTH as i32);
sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, fbp0.as_mut_ptr_from_zero() as _, BUF_WIDTH as i32);
sys::sceGuDispBuffer(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32, fbp1.as_mut_ptr_from_zero() as _, BUF_WIDTH as i32);
sys::sceGuDepthBuffer(zbp.as_mut_ptr_from_zero() as _, BUF_WIDTH as i32);
sys::sceGuOffset(2048 - (SCREEN_WIDTH / 2), 2048 - (SCREEN_HEIGHT / 2));
sys::sceGuViewport(2048, 2048, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);
sys::sceGuDepthRange(65535, 0);
Expand Down
53 changes: 39 additions & 14 deletions psp/src/vram_alloc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::sys::TexturePixelFormat;
use crate::sys::{sceGeEdramGetAddr, sceGeEdramGetSize};
use core::marker::PhantomData;
use core::mem::size_of;
use core::ptr::null_mut;
use core::sync::atomic::{AtomicU32, Ordering};

type VramAllocator = SimpleVramAllocator;

Expand All @@ -27,14 +29,20 @@ impl VramAllocatorSingleton {
}
}

pub struct VramMemChunk {
pub struct VramMemChunk<'a> {
start: u32,
len: u32,
// Needed since VramMemChunk has a lifetime, but doesn't contain references
vram: PhantomData<&'a mut ()>,
}

impl VramMemChunk {
impl VramMemChunk<'_> {
fn new(start: u32, len: u32) -> Self {
Self { start, len }
Self {
start,
len,
vram: PhantomData,
}
}

pub fn as_mut_ptr_from_zero(&self) -> *mut u8 {
Expand All @@ -54,38 +62,55 @@ impl VramMemChunk {
// TODO: pin?
#[derive(Debug)]
pub struct SimpleVramAllocator {
offset: u32,
offset: AtomicU32,
}

impl SimpleVramAllocator {
const fn new() -> Self {
Self { offset: 0 }
Self {
offset: AtomicU32::new(0),
}
}

// TODO: return a Result instead of panicking
pub fn alloc(&mut self, size: u32) -> VramMemChunk {
let old_offset = self.offset;
self.offset += size;
/// Frees all previously allocated VRAM chunks.
///
/// This resets the allocator's counter, but does not change the contents of
/// VRAM. Since this method requires `&mut Self`, it cannot overlap with any
/// previously allocated `VramMemChunk`s since they have the lifetime of the
/// `&Self` that allocated them.
pub fn free_all(&mut self) {
self.offset.store(0, Ordering::Relaxed);
}

if self.offset > self.total_mem() {
// TODO: return a Result instead of panicking
/// Allocates `size` bytes of VRAM
///
/// The returned VRAM chunk has the same lifetime as the
/// `SimpleVramAllocator` borrow (i.e. `&self`) that allocated it.
pub fn alloc<'a>(&'a self, size: u32) -> VramMemChunk<'a> {
let old_offset = self.offset.load(Ordering::Relaxed);
let new_offset = old_offset + size;
self.offset.store(new_offset, Ordering::Relaxed);
Comment on lines +91 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a good old race condition right there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional btw? Perhaps this was written as a singlethreaded code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleVramAllocator is used as a singleton, so I was treating this like singlethreaded code. The AtomicU32 is only for interior mutability and I went with that over Cell to make it easier in case anyone wants to try using the allocator from multiple threads (though that would require a few more changes).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the problem is SimpleVramAllocator implements Sync, which means you can share references of it between different threads, and function in question (alloc) requires just a shared reference, so alloc can be simultaneously run on multiple different threads. in that case alloc can return two aliasing mem chunks.

Copy link
Contributor Author

@ayrtonm ayrtonm Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In bumpalo thread support is provided by a "herd" which is roughly a Mutex<Vec<Bump>> that creates a new bump allocator with its own pool of memory for each thread. Splitting VRAM per thread seems impractical and I don't think there's a safe way to use one bump allocator from multiple threads. So I'd say let's just add impl !Sync for SimpleVramAllocator in #120 for now.

Copy link
Contributor

@zetanumbers zetanumbers Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just add impl !Sync for SimpleVramAllocator

No, i think have figured atomics out. It's just nvidia-driver-390 killed my os, so i am on windows for now, that takes it this long and i am back on linux.


if new_offset > self.total_mem() {
panic!("Total VRAM size exceeded!");
}

VramMemChunk::new(old_offset, size)
}

// TODO: ensure 16-bit alignment?
pub fn alloc_sized<T: Sized>(&mut self, count: u32) -> VramMemChunk {
pub fn alloc_sized<'a, T: Sized>(&'a self, count: u32) -> VramMemChunk<'a> {
let size = size_of::<T>() as u32;
self.alloc(count * size)
}

pub fn alloc_texture_pixels(
&mut self,
pub fn alloc_texture_pixels<'a>(
&'a self,
width: u32,
height: u32,
psm: TexturePixelFormat,
) -> VramMemChunk {
) -> VramMemChunk<'a> {
let size = get_memory_size(width, height, psm);
self.alloc(size)
}
Expand Down