From 5666f18ded995fa434583bedd32220c119d0b8cc Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 9 Jan 2020 01:05:20 -0500 Subject: [PATCH] Add terrain meshing benchmark --- Cargo.lock | 2 + voxygen/Cargo.toml | 11 +++ voxygen/benches/meshing_benchmark.rs | 126 +++++++++++++++++++++++++++ voxygen/src/lib.rs | 3 + world/src/lib.rs | 1 + 5 files changed, 143 insertions(+) create mode 100644 voxygen/benches/meshing_benchmark.rs create mode 100644 voxygen/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c8f438c727..56f141905c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 3e497aef74..1760929b2f 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -4,6 +4,9 @@ version = "0.4.0" authors = ["Joshua Barretto ", "Imbris "] 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 diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs new file mode 100644 index 0000000000..4025e9f031 --- /dev/null +++ b/voxygen/benches/meshing_benchmark.rs @@ -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 = 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| { + 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); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs new file mode 100644 index 0000000000..f3fad20a8b --- /dev/null +++ b/voxygen/src/lib.rs @@ -0,0 +1,3 @@ +/// Used by benchmarks +pub mod mesh; +pub mod render; diff --git a/world/src/lib.rs b/world/src/lib.rs index 745515cd54..cf68981989 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -63,6 +63,7 @@ impl World { pub fn generate_chunk( &self, chunk_pos: Vec2, + // TODO: misleading name mut should_continue: impl FnMut() -> bool, ) -> Result<(TerrainChunk, ChunkSupplement), ()> { let air = Block::empty();