use super::BenchWorld;
use crate::components::{
Behavior, BehaviorExpr, BehaviorNode, BehaviorSource, Collider, GlobalTransform, PhysicsConfig,
Prop, PropCollider, RenderHandle, Transform,
};
use crate::ecs::SYSTEMS;
use crate::ecs::World;
use crate::gfx::graphics_system::GraphicsSystem;
use crate::gfx::snapshot::RenderSnapshot;
const WARMUP_FRAMES: usize = 64;
const QUIET_FRAME_DEADLINE: std::time::Duration = std::time::Duration::from_secs(10);
const STATIC_WORLD_ALLOCS_PER_FRAME: u64 = 0;
fn quietest_frame(budget: u64, mut step: impl FnMut()) -> u64 {
let deadline = std::time::Instant::now() + QUIET_FRAME_DEADLINE;
let mut min = u64::MAX;
loop {
let before = concinnity_core::memory::alloc_count().expect("test binary tracks its heap");
step();
let after = concinnity_core::memory::alloc_count().expect("allocator stays installed");
min = min.min(after - before);
if min <= budget || std::time::Instant::now() >= deadline {
return min;
}
}
}
fn static_world() -> World {
let mut world = World::new();
world.add_component(Behavior {
on: BehaviorSource::Tick,
body: vec![BehaviorNode::Set {
var: "beat".into(),
value: BehaviorExpr::Int(1),
add: true,
}],
..Default::default()
});
for i in 0..200 {
world.add_component(Prop {
position: [i as f32 * 2.0, 0.5, 0.0],
..Default::default()
});
}
world.add_component(PhysicsConfig::default());
for i in 0..8 {
let e = world.push(Transform {
position: [i as f32 * 25.0, 0.0, 0.0],
rotation_deg: [0.0; 3],
scale: [1.0; 3],
});
world.insert(
e,
Collider(PropCollider {
shape: "cuboid".into(),
half_extents: [10.0, 0.5, 10.0],
..Default::default()
}),
);
}
world
}
#[test]
fn static_world_frame_allocs_stay_pinned() {
let mut world = static_world();
world.start(SYSTEMS).unwrap();
for _ in 0..WARMUP_FRAMES {
world.step();
}
let min = quietest_frame(STATIC_WORLD_ALLOCS_PER_FRAME, || {
world.step();
});
assert_eq!(
min,
STATIC_WORLD_ALLOCS_PER_FRAME,
"static world's quietest frame allocated {min} times \
(pinned at {STATIC_WORLD_ALLOCS_PER_FRAME}); a per-frame allocation crept in.\n\
last frame by system: {:?}",
world.profile().system_allocs(),
);
}
#[test]
fn static_extraction_allocates_nothing() {
let mut world = BenchWorld::new();
for i in 0..100u32 {
let entity = world.components.push_typed(Prop::default());
world
.components
.insert_typed(entity, GlobalTransform(glam_identity_at(i)));
world
.components
.insert_typed(entity, RenderHandle { draws: [i].into() });
}
let mut gs = GraphicsSystem::new(None);
let mut snap = RenderSnapshot::default();
for _ in 0..WARMUP_FRAMES {
gs.extract(&mut world.ctx(), &mut snap);
}
let min = quietest_frame(0, || {
gs.extract(&mut world.ctx(), &mut snap);
});
assert_eq!(
min, 0,
"static extraction's quietest pass allocated {min} times; \
the zero-alloc extraction contract broke"
);
}
fn glam_identity_at(i: u32) -> [[f32; 4]; 4] {
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[i as f32, 0.0, 0.0, 1.0],
]
}
#[cfg(debug_assertions)]
#[test]
fn frame_loop_samples_alloc_deltas_into_the_profile() {
let mut world = static_world();
world.start(SYSTEMS).unwrap();
world.step();
world.step();
let profile = world.profile();
assert!(profile.frame_allocs().is_some());
let timed: Vec<&str> = profile.system_timings().iter().map(|&(n, _)| n).collect();
let counted: Vec<&str> = profile.system_allocs().iter().map(|&(n, _)| n).collect();
assert_eq!(timed, counted, "alloc entries mirror the timing entries");
assert!(!counted.is_empty(), "the static world runs systems");
}