pub struct FrameArena { /* private fields */ }Expand description
A fixed-capacity bump allocator for per-frame data.
§Growth policy
The arena never grows. Exhaustion is a hard error: the allocating
methods panic and the try_ variants return None. Two reasons:
- A frame arena that grows on demand cannot be distinguished from a leak. The whole point is that per-frame allocation is bounded; if the bound is soft, the first system that forgets to clamp a list quietly turns into a memory-growth bug that only shows up as a stall days later.
- Growth would invalidate outstanding slices, so a growing arena needs
either chunk chaining or
unsafealiasing games. Neither is worth it at this layer.
The peak in FrameArena::stats is the tool for sizing: run the game, read
the high-water mark, set the capacity with headroom. Systems that can
legitimately degrade (a debug overlay, a particle burst) should use
FrameArena::try_alloc_slice and drop work when the budget is gone rather
than take the process down.
§Borrowing
Allocation takes &self and returns &mut [T] borrowed from the arena, so
any number of allocations can be live at once — which is the actual
usage: a frame system builds a visible-instance list and a draw-argument
list and an index list, and holds all three until the encoder consumes
them. FrameArena::reset takes &mut self, so the borrow checker is what
proves no slice outlives its frame; the frame boundary needs no discipline
beyond calling reset.
use crcbl_core::alloc::FrameArena;
let mut arena = FrameArena::with_capacity(4096);
let instances = arena.alloc_slice(3, [0.0f32; 4]);
let draw_args = arena.alloc_slice(2, 0u32);
instances[0][1] = 1.0;
draw_args[1] = 7;
// Both live at once; `reset` cannot be called until both are dropped.
assert_eq!(instances[0][1] + draw_args[1] as f32, 8.0);
arena.reset();§Aliasing
This is the bumpalo model, and handing &mut out of &self rests on
three facts:
- Regions are disjoint by construction. The bump offset only ever advances between resets, so no two allocations can return overlapping byte ranges. Every returned slice is the unique reference to its bytes.
- The block never moves and never grows. Capacity is fixed at
construction and the backing
Vecis never reborrowed afterwards — every allocation derives frombase, a pointer taken once inwith_capacity. (CallingVec::as_mut_ptragain would create a fresh unique reference to the whole block and invalidate every slice already handed out.) resetandreset_peaktake&mut self. An exclusive borrow of the arena cannot coexist with any slice borrowed from it, so memory can only be recycled once every reference to it is gone.T: Copyalso means no allocation has drop glue to skip.
The arena is neither Send nor Sync: allocation through &self is
unsynchronized, and the Cell bump offset makes that a compile error rather
than a data race. A job system wants one arena per worker anyway.
Implementations§
Source§impl FrameArena
impl FrameArena
Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Creates an arena with at least capacity bytes, rounded up to a
multiple of the 64-byte block size.
Sourcepub fn used(&self) -> usize
pub fn used(&self) -> usize
Bytes handed out since the last FrameArena::reset.
Sourcepub fn stats(&self) -> ArenaStats
pub fn stats(&self) -> ArenaStats
Used / capacity / peak.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Frees everything allocated this frame. O(1); the memory is not touched, so the next frame reads whatever the last one wrote until it initializes its own data.
Takes &mut self: this is the frame boundary, and the exclusive borrow
is what proves every slice from the last frame is already gone.
The high-water mark survives — that is the point of tracking it.
Sourcepub fn reset_peak(&mut self)
pub fn reset_peak(&mut self)
Forgets the high-water mark (e.g. when entering a new level and the old peak stops being informative).
Sourcepub fn alloc_slice<T: Copy>(&self, len: usize, value: T) -> &mut [T]
pub fn alloc_slice<T: Copy>(&self, len: usize, value: T) -> &mut [T]
Allocates len copies of value.
§Panics
If the arena is exhausted — see the growth policy in the type docs — or
if T needs more than 64-byte alignment.
Sourcepub fn try_alloc_slice<T: Copy>(&self, len: usize, value: T) -> Option<&mut [T]>
pub fn try_alloc_slice<T: Copy>(&self, len: usize, value: T) -> Option<&mut [T]>
Allocates len copies of value, or None if the arena is exhausted.
§Panics
If T needs more than 64-byte alignment — that is a programming error,
not a budget overrun, so it is not reported through the Option.