use super::{BenchWorld, bench};
use crate::components::{GlobalTransform, Parent, Prop, Transform};
use crate::ecs::Entity;
use crate::gfx::transform_propagation::{TransformCache, propagate_transforms_cached};
const FLAT: usize = 10_000;
const CHAINS: usize = 1_250;
const CHAIN_DEPTH: usize = 8;
fn transform_at(i: usize) -> Transform {
Transform {
position: [i as f32, 0.45, 0.0],
rotation_deg: [0.0; 3],
scale: [1.0; 3],
}
}
fn spawn(world: &mut BenchWorld, i: usize, parent: Option<Entity>) -> Entity {
let entity = world.components.push_typed(Prop::default());
world.components.insert_typed(entity, transform_at(i));
world
.components
.insert_typed(entity, GlobalTransform::default());
if let Some(p) = parent {
world.components.insert_typed(entity, Parent(p));
}
entity
}
fn flat_world(count: usize) -> (BenchWorld, Vec<Entity>) {
let mut world = BenchWorld::new();
let entities = (0..count).map(|i| spawn(&mut world, i, None)).collect();
(world, entities)
}
fn chained_world(chains: usize, depth: usize) -> (BenchWorld, Vec<Entity>) {
let mut world = BenchWorld::new();
let mut entities = Vec::with_capacity(chains * depth);
for c in 0..chains {
let mut parent = None;
for d in 0..depth {
let entity = spawn(&mut world, c * depth + d, parent);
entities.push(entity);
parent = Some(entity);
}
}
(world, entities)
}
#[test]
#[ignore = "microbench; see crate::bench for the command"]
fn transform_propagation() {
println!("\ntransform propagation");
{
let (mut world, _) = flat_world(FLAT);
let mut cache = TransformCache::default();
propagate_transforms_cached(&mut world.ctx(), &mut cache);
bench("cached_static/10k", FLAT as u64, || {
propagate_transforms_cached(&mut world.ctx(), &mut cache);
});
}
{
let (mut world, entities) = flat_world(FLAT);
let mut cache = TransformCache::default();
let first = entities[0];
bench("dirty_flat/10k", FLAT as u64, || {
if let Some(t) = world.ctx().get_mut::<Transform>(first) {
t.position[1] += 0.001;
}
propagate_transforms_cached(&mut world.ctx(), &mut cache);
});
}
{
let (mut world, entities) = chained_world(CHAINS, CHAIN_DEPTH);
let mut cache = TransformCache::default();
let leaf = entities[CHAIN_DEPTH - 1];
let count = (CHAINS * CHAIN_DEPTH) as u64;
bench(
&format!("dirty_chains_depth{CHAIN_DEPTH}/10k"),
count,
|| {
if let Some(t) = world.ctx().get_mut::<Transform>(leaf) {
t.position[1] += 0.001;
}
propagate_transforms_cached(&mut world.ctx(), &mut cache);
},
);
}
{
let (mut world, entities) = chained_world(CHAINS, CHAIN_DEPTH);
let mut cache = TransformCache::default();
let root = entities[0];
let count = (CHAINS * CHAIN_DEPTH) as u64;
bench(
&format!("dirty_chain_root_depth{CHAIN_DEPTH}/10k"),
count,
|| {
if let Some(t) = world.ctx().get_mut::<Transform>(root) {
t.position[1] += 0.001;
}
propagate_transforms_cached(&mut world.ctx(), &mut cache);
},
);
}
{
let (mut world, entities) = flat_world(FLAT);
let mut cache = TransformCache::default();
let moved: Vec<Entity> = entities.iter().step_by(4).copied().collect();
bench("dirty_quarter_flat/10k", FLAT as u64, || {
for &e in &moved {
if let Some(t) = world.ctx().get_mut::<Transform>(e) {
t.position[1] += 0.001;
}
}
propagate_transforms_cached(&mut world.ctx(), &mut cache);
});
}
{
let (mut world, _) = flat_world(FLAT);
let mut cache = TransformCache::default();
bench("full_resolve_flat/10k", FLAT as u64, || {
world.ctx().query_slice_mut::<Transform>()[0].position[1] += 0.001;
propagate_transforms_cached(&mut world.ctx(), &mut cache);
});
}
{
let (mut world, _) = chained_world(CHAINS, CHAIN_DEPTH);
let mut cache = TransformCache::default();
let count = (CHAINS * CHAIN_DEPTH) as u64;
bench(
&format!("full_resolve_chains_depth{CHAIN_DEPTH}/10k"),
count,
|| {
world.ctx().query_slice_mut::<Transform>()[0].position[1] += 0.001;
propagate_transforms_cached(&mut world.ctx(), &mut cache);
},
);
}
}