Add terrain meshing benchmark

This commit is contained in:
Imbris 2020-01-09 01:05:20 -05:00
parent 6f6dc1b16b
commit 5666f18ded
5 changed files with 143 additions and 0 deletions

2
Cargo.lock generated
View File

@ -3248,6 +3248,7 @@ dependencies = [
"conrod_core 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
"conrod_winit 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
"cpal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3279,6 +3280,7 @@ dependencies = [
"veloren-client 0.4.0",
"veloren-common 0.4.0",
"veloren-server 0.4.0",
"veloren-world 0.4.0",
"winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -4,6 +4,9 @@ version = "0.4.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Imbris <imbrisf@gmail.com>"]
edition = "2018"
default-run = "veloren-voxygen"
# Cargo thinks it should build the voxygen binary even when a specific bench is specified for building
# Uncomment below and comment out default-run if you want to avoid this
# autobins = false
[features]
gl = ["gfx_device_gl"]
@ -65,3 +68,11 @@ dispatch = "0.1.4"
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[dev-dependencies]
criterion = "0.3"
world = { package = "veloren-world", path = "../world" }
[[bench]]
name = "meshing_benchmark"
harness = false

View File

@ -0,0 +1,126 @@
use common::terrain::Block;
use common::terrain::TerrainGrid;
use common::vol::SampleVol;
use common::vol::Vox;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::sync::Arc;
use vek::*;
use veloren_voxygen::mesh::Meshable;
use world::World;
const CENTER: Vec2<i32> = Vec2 { x: 512, y: 512 };
const GEN_SIZE: i32 = 4;
pub fn criterion_benchmark(c: &mut Criterion) {
// Generate chunks here to test
let mut terrain = TerrainGrid::new().unwrap();
let world = World::generate(42);
(0..GEN_SIZE)
.flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y)))
.map(|offset| offset + CENTER)
.map(|pos| (pos, world.generate_chunk(pos, || false).unwrap()))
.for_each(|(key, chunk)| {
terrain.insert(key, Arc::new(chunk.0));
});
let sample = |chunk_pos: Vec2<i32>| {
let chunk_pos = chunk_pos + CENTER;
// Find the area of the terrain we want. Because meshing needs to compute things like
// ambient occlusion and edge elision, we also need the borders of the chunk's
// neighbours too (hence the `- 1` and `+ 1`).
let aabr = Aabr {
min: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| e * sz as i32 - 1),
max: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
};
// Copy out the chunk data we need to perform the meshing. We do this by taking a
// sample of the terrain that includes both the chunk we want and its neighbours.
let volume = terrain.sample(aabr).unwrap();
// The region to actually mesh
let min_z = volume
.iter()
.fold(std::i32::MAX, |min, (_, chunk)| chunk.get_min_z().min(min));
let max_z = volume
.iter()
.fold(std::i32::MIN, |max, (_, chunk)| chunk.get_max_z().max(max));
let aabb = Aabb {
min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 1),
max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 1),
};
(volume, aabb)
};
// Test speed of cloning voxel sample into a flat array
let (volume, range) = sample(Vec2::new(1, 1));
c.bench_function("copying 1,1 into flat array", |b| {
b.iter(|| {
let mut flat = vec![Block::empty(); range.size().product() as usize];
let mut i = 0;
let mut volume = volume.cached();
for x in 0..range.size().w {
for y in 0..range.size().h {
for z in 0..range.size().d {
flat[i] = *volume.get(range.min + Vec3::new(x, y, z)).unwrap();
i += 1;
}
}
}
/*let (w, h, d) = range.size().into_tuple();
for (chunk_key, chunk) in volume.iter() {
let chunk_pos = volume.key_pos(chunk_key);
let min = chunk_pos.map2(
Vec2::new(range.min.x, range.min.y),
|cmin: i32, rmin: i32| (rmin - cmin).max(0),
);
// Chunk not in area of interest
if min
.map2(TerrainGrid::chunk_size(), |m, size| m >= size as i32)
.reduce_and()
{
// TODO: comment after ensuing no panics
panic!("Shouldn't happen in this case");
continue;
}
let min = min.map(|m| m.min(31));
// TODO: Don't hardcode 31
let max = chunk_pos.map2(Vec2::new(range.max.x, range.max.y), |cmin, rmax| {
(rmax - cmin).min(31)
});
if max.map(|m| m < 0).reduce_and() {
panic!("Shouldn't happen in this case: {:?}", max);
continue;
}
let max = max.map(|m| m.max(0));
// Add z dims
let min = Vec3::new(min.x, min.y, range.min.z);
let max = Vec3::new(max.x, max.y, range.max.z);
// Offset of chunk in sample being cloned
let offset = Vec3::new(
chunk_pos.x - range.min.x,
chunk_pos.y - range.min.y,
-range.min.z,
);
for (pos, &block) in chunk.vol_iter(min, max) {
let pos = pos + offset;
flat[(w * h * pos.z + w * pos.y + pos.x) as usize] = block;
}
}*/
black_box(flat);
});
});
for x in 1..GEN_SIZE - 1 {
for y in 1..GEN_SIZE - 1 {
let (volume, range) = sample(Vec2::new(x, y));
c.bench_function(&format!("Terrain mesh {}, {}", x, y), |b| {
b.iter(|| volume.generate_mesh(black_box(range)))
});
}
}
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

3
voxygen/src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
/// Used by benchmarks
pub mod mesh;
pub mod render;

View File

@ -63,6 +63,7 @@ impl World {
pub fn generate_chunk(
&self,
chunk_pos: Vec2<i32>,
// TODO: misleading name
mut should_continue: impl FnMut() -> bool,
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
let air = Block::empty();