makepad/libs/xatlas/tests/bench_sizes.rs
Admin fbe877d746 Land glTF writers, remesh, PBR paint, and xatlas from rik2.
Animation/skin writers, COLOR_0, and the mesh tools the renderer uses.
2026-08-18 14:23:58 +02:00

134 lines
4.3 KiB
Rust

//! Wall-clock of `parametrize` on remesh-sized meshes. Run:
//! cargo test -p makepad-xatlas --release --test bench_sizes -- --nocapture --ignored
use makepad_xatlas::parametrize;
use std::time::Instant;
fn grid(cols: u32, rows: u32) -> (Vec<[f32; 3]>, Vec<[u32; 3]>) {
let mut positions = Vec::new();
for y in 0..=rows {
for x in 0..=cols {
positions.push([x as f32, y as f32, 0.0]);
}
}
let mut faces = Vec::new();
let w = cols + 1;
for y in 0..rows {
for x in 0..cols {
let i = y * w + x;
faces.push([i, i + 1, i + w + 1]);
faces.push([i, i + w + 1, i + w]);
}
}
(positions, faces)
}
/// Closed box with per-face verts, then tessellated so xatlas has 6 charts
/// plus internal splits — closer to a remeshed prop than a flat grid.
fn tess_cube(div: u32) -> (Vec<[f32; 3]>, Vec<[u32; 3]>) {
let faces = [
([1.0f32, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]),
([-1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0]),
([0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]),
([0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]),
([0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]),
([0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]),
];
let mut positions = Vec::new();
let mut tris = Vec::new();
for (n, u, v) in faces {
let base = positions.len() as u32;
for j in 0..=div {
for i in 0..=div {
let su = (i as f32 / div as f32) - 0.5;
let sv = (j as f32 / div as f32) - 0.5;
positions.push([
n[0] * 0.5 + u[0] * su + v[0] * sv,
n[1] * 0.5 + u[1] * su + v[1] * sv,
n[2] * 0.5 + u[2] * su + v[2] * sv,
]);
}
}
let w = div + 1;
for j in 0..div {
for i in 0..div {
let i0 = base + j * w + i;
tris.push([i0, i0 + 1, i0 + w + 1]);
tris.push([i0, i0 + w + 1, i0 + w]);
}
}
}
(positions, tris)
}
/// Heightfield with enough normal variation that xatlas grows many charts.
fn noisy_terrain(cols: u32, rows: u32) -> (Vec<[f32; 3]>, Vec<[u32; 3]>) {
let mut positions = Vec::new();
for y in 0..=rows {
for x in 0..=cols {
let xf = x as f32 * 0.08;
let yf = y as f32 * 0.08;
let z = (xf * 1.7).sin() * 0.35
+ (yf * 2.3).cos() * 0.28
+ ((xf + yf) * 4.1).sin() * 0.12
+ ((xf * 9.0).sin() * (yf * 8.0).cos()) * 0.08;
positions.push([xf, yf, z]);
}
}
let mut faces = Vec::new();
let w = cols + 1;
for y in 0..rows {
for x in 0..cols {
let i = y * w + x;
faces.push([i, i + 1, i + w + 1]);
faces.push([i, i + w + 1, i + w]);
}
}
(positions, faces)
}
fn time_one(name: &str, positions: &[[f32; 3]], faces: &[[u32; 3]]) {
let t0 = Instant::now();
let out = parametrize(positions, faces).expect(name);
let dt = t0.elapsed();
eprintln!(
"{name:>16} in_v={:<6} in_f={:<6} out_v={:<6} charts={:<4} atlas={}x{} {:>8.3}s",
positions.len(),
faces.len(),
out.vertices.len(),
out.chart_count,
out.width,
out.height,
dt.as_secs_f64()
);
}
#[test]
#[ignore]
fn time_remesh_sized_unwraps() {
// Warm the allocator / caches.
let (p, f) = tess_cube(4);
let _ = parametrize(&p, &f);
// Flat grid: few charts (optimistic).
let (p, f) = grid(77, 77); // ~12k tris
time_one("grid 12k", &p, &f);
let (p, f) = grid(100, 100); // 20k tris
time_one("grid 20k", &p, &f);
// Tessellated cube: 6 charts, prop/character densities.
// 6 * 2 * d^2 faces.
let (p, f) = tess_cube(32); // 6*2*1024 = 12288
time_one("cube 12k", &p, &f);
let (p, f) = tess_cube(41); // 6*2*1681 = 20172
time_one("cube 20k", &p, &f);
let (p, f) = tess_cube(82); // 6*2*6724 = 80688
time_one("cube 80k", &p, &f);
let (p, f) = noisy_terrain(77, 77);
time_one("terrain 12k", &p, &f);
let (p, f) = noisy_terrain(100, 100);
time_one("terrain 20k", &p, &f);
let (p, f) = noisy_terrain(200, 200);
time_one("terrain 80k", &p, &f);
}