1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Arena configuration parameters.
/// Configuration for the arena allocator.
///
/// Controls segment sizing, capacity limits, and generation retention.
/// Validated at construction; all values are immutable after creation.
#[derive(Clone, Debug)]
#[must_use]
pub struct ArenaConfig {
/// Size of each arena segment in f32 elements.
///
/// Default: 16_777_216 (64MB at 4 bytes per f32).
/// Must be a power of two and at least 1024.
pub segment_size: u32,
/// Maximum number of segments across all three pools combined
/// (per-tick buffer A + per-tick buffer B + sparse).
///
/// The budget is divided: each per-tick buffer gets `max_segments / 3`,
/// and the sparse pool gets the remainder. For example, with the default
/// of 16: each per-tick buffer gets 5 segments, sparse gets 6.
///
/// Must be at least 3 (one segment per pool). [`PingPongArena::new`]
/// returns [`ArenaError::InvalidConfig`] if this constraint is violated.
///
/// Default: 16. Each segment is `segment_size * 4` bytes, so 16 segments
/// at the default size = 1GB total arena capacity.
///
/// [`PingPongArena::new`]: crate::PingPongArena::new
/// [`ArenaError::InvalidConfig`]: crate::ArenaError::InvalidConfig
pub max_segments: u16,
/// How many generations a handle remains valid after creation.
///
/// For Lockstep mode this is 1 (only the current and previous generation
/// are live). For RealtimeAsync this matches the ring buffer capacity.
pub max_generation_age: u32,
/// Number of cells in the simulation grid.
///
/// Used to compute per-field allocation sizes:
/// `field_len = cell_count * field_type.components()`.
pub cell_count: u32,
}
impl ArenaConfig {
/// Default segment size: 64MB / 4 bytes = 16M f32 elements.
pub const DEFAULT_SEGMENT_SIZE: u32 = 16_777_216;
/// Default maximum segment count.
pub const DEFAULT_MAX_SEGMENTS: u16 = 16;
/// Default generation age for Lockstep mode.
pub const DEFAULT_MAX_GENERATION_AGE: u32 = 1;
/// Create a new arena config for the given cell count.
///
/// Uses default values for all other parameters.
pub fn new(cell_count: u32) -> Self {
Self {
segment_size: Self::DEFAULT_SEGMENT_SIZE,
max_segments: Self::DEFAULT_MAX_SEGMENTS,
max_generation_age: Self::DEFAULT_MAX_GENERATION_AGE,
cell_count,
}
}
/// Total capacity of a single segment in bytes.
pub fn segment_bytes(&self) -> usize {
self.segment_size as usize * std::mem::size_of::<f32>()
}
}
impl Default for ArenaConfig {
fn default() -> Self {
Self::new(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_segment_size_is_64mb() {
let config = ArenaConfig::new(100);
assert_eq!(config.segment_bytes(), 64 * 1024 * 1024);
}
#[test]
fn cell_count_preserved() {
let config = ArenaConfig::new(10_000);
assert_eq!(config.cell_count, 10_000);
}
}