Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

Module frame

Module frame 

Source
Expand description

Borrow-checked frame building over an ordering table.

The GPU’s linked-list DMA reads every packet of a frame after the CPU has handed it over, so a packet must outlive the walk and must not change during it. This module states that with lifetimes instead of unsafe:

Every type here is a thin wrapper over the same stores and kicks as OtFrame::add_raw and crate::chain::submit_raw.

§Usage

use psx_gpu::frame::{FramePair, FrameStorage, PrimitiveArena};
use psx_gpu::{ot::OrderingTable, prim::TriFlat};

fn blocking(ot: &mut OrderingTable<8>, dma: &mut psx_io::periph::GpuDma) {
    let mut storage: [TriFlat; 4] =
        core::array::from_fn(|_| TriFlat::new([(0, 0); 3], 0, 0, 0));
    let mut frame = ot.frame();
    let mut arena = PrimitiveArena::new(&mut storage);
    if let Some(tri) = arena.push(TriFlat::new([(0, 0), (8, 0), (0, 8)], 255, 0, 0)) {
        frame.add(1, tri);
    }
    frame.submit(dma);
    // The walk is over: the table and the storage are free again.
    ot.clear();
    storage[0] = TriFlat::new([(0, 0); 3], 0, 0, 0);
}

fn overlapped(
    storage: &'static mut FrameStorage<8, [TriFlat; 4]>,
    dma: psx_io::periph::GpuDma,
) {
    let (in_flight, added) = storage.draw_async(dma, |frame, packets| {
        let mut arena = PrimitiveArena::new(packets);
        let tri = arena.push(TriFlat::new([(0, 0), (8, 0), (0, 8)], 0, 255, 0));
        tri.map(|tri| frame.add(1, tri)).is_some()
    });
    // ... CPU work while the GPU walks ...
    let (storage, dma) = in_flight.wait();
    let mut pair = FramePair::new(storage, other_storage(), dma);
    pair.present(|_frame, _packets| {});
}

§What the borrow checker rejects

A packet that dies before the frame is submitted:

ⓘ
let mut ot = OrderingTable::<8>::new();
let mut frame = ot.frame();
{
    let mut tri = TriFlat::new([(0, 0), (8, 0), (0, 8)], 255, 0, 0);
    frame.add(1, &mut tri);
} // `tri` dropped here while the frame still links it
frame.submit(dma);

Clearing (or otherwise touching) the table while its walk is in flight:

ⓘ
let mut ot = OrderingTable::<8>::new();
let frame = ot.frame();
frame.submit_with(dma, || ot.clear());

Reusing the storage of an asynchronously kicked frame before waiting:

ⓘ
let (in_flight, ()) = storage.draw_async(dma, |_frame, _packets| {});
storage.draw_async(unsafe { psx_io::periph::GpuDma::steal() }, |_frame, _packets| {});
in_flight.wait();

Restarting an arena over storage whose packets are still linked:

ⓘ
let mut ot = OrderingTable::<8>::new();
let mut storage: [TriFlat; 4] = core::array::from_fn(|_| TriFlat::new([(0, 0); 3], 0, 0, 0));
let mut frame = ot.frame();
let mut arena = PrimitiveArena::new(&mut storage);
frame.add(1, arena.push(TriFlat::new([(0, 0), (8, 0), (0, 8)], 255, 0, 0)).unwrap());
let mut again = PrimitiveArena::new(&mut storage); // still borrowed by the frame
again.push(TriFlat::new([(0, 0); 3], 0, 0, 0));
frame.submit(dma);

Structs§

FramePair
Two FrameStorages in the usual ping-pong: build frame N+1 into one while the GPU walks frame N from the other.
FrameStorage
An ordering table together with the packet storage its frames link.
InFlight
A frame the GPU is walking, owning its 'static storage and the GpuDma token until wait.
OtFrame
One frame’s view of an OrderingTable: insert packets, then submit.
PrimitiveArena
Hands out packets from caller storage as &'f mut P, one slot each.