mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
32fdcc1c1c
I'm pretty sure this bug already existed on master, so if the performance degradation is unacceptable, we can revert.
391 lines
15 KiB
Rust
391 lines
15 KiB
Rust
use common::{
|
|
generation::EntityInfo,
|
|
store::{Id, Store},
|
|
terrain::{Block, BlockKind, SpriteKind, TerrainSubChunk, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
|
vol::RectVolSize,
|
|
};
|
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
use hashbrown::HashMap;
|
|
use rand::prelude::*;
|
|
use rayon::ThreadPoolBuilder;
|
|
use vek::{Aabr, Rgb, Vec2, Vec3};
|
|
use veloren_world::{
|
|
config::CONFIG,
|
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
|
site2::{plot::PlotKind, Fill, Filler, Plot, Primitive, Site, Structure},
|
|
CanvasInfo, Land, World,
|
|
};
|
|
|
|
/* #[allow(dead_code)]
|
|
fn count_prim_kinds(prims: &Store<Primitive>) -> HashMap<String, usize> {
|
|
let mut ret = HashMap::new();
|
|
for prim in prims.values() {
|
|
match &prim {
|
|
Primitive::Empty => {
|
|
*ret.entry("Empty".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Aabb(_) => {
|
|
*ret.entry("Aabb".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Pyramid { .. } => {
|
|
*ret.entry("Pyramid".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Ramp { .. } => {
|
|
*ret.entry("Ramp".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Gable { .. } => {
|
|
*ret.entry("Gable".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Cylinder(_) => {
|
|
*ret.entry("Cylinder".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Cone(_) => {
|
|
*ret.entry("Cone".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Sphere(_) => {
|
|
*ret.entry("Sphere".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Superquadric { .. } => {
|
|
*ret.entry("Superquadratic".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Plane(_, _, _) => {
|
|
*ret.entry("Plane".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Segment { .. } => {
|
|
*ret.entry("Segment".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::SegmentPrism { .. } => {
|
|
*ret.entry("SegmentPrism".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Sampling(_, _) => {
|
|
*ret.entry("Sampling".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Prefab(_) => {
|
|
*ret.entry("Prefab".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Intersect(_, _) => {
|
|
*ret.entry("Intersect".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Union(_, _) => {
|
|
*ret.entry("Union".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Without(_, _) => {
|
|
*ret.entry("Without".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::RotateAbout(_, _, _) => {
|
|
*ret.entry("RotateAbout".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Translate(_, _) => {
|
|
*ret.entry("Translate".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Scale(_, _) => {
|
|
*ret.entry("Scale".to_string()).or_default() += 1;
|
|
},
|
|
Primitive::Repeat(_, _, _) => {
|
|
*ret.entry("Repeat".to_string()).or_default() += 1;
|
|
},
|
|
}
|
|
}
|
|
ret
|
|
} */
|
|
|
|
fn render_plots<'a>(/*canvas: &mut Canvas<'a>,*/
|
|
arena: &'a mut bumpalo::Bump,
|
|
info: CanvasInfo<'a>,
|
|
site: &'a Site,
|
|
render_area: Aabr<i32>,
|
|
) {
|
|
for plot in site.plots() {
|
|
let structure: &dyn Structure<_> = match plot.kind() {
|
|
PlotKind::House(house) => house,
|
|
PlotKind::Workshop(workshop) => workshop,
|
|
PlotKind::Castle(castle) => castle,
|
|
PlotKind::Dungeon(dungeon) => dungeon,
|
|
PlotKind::Gnarling(gnarling) => gnarling,
|
|
PlotKind::GiantTree(giant_tree) => giant_tree,
|
|
PlotKind::CliffTower(cliff_tower) => cliff_tower,
|
|
PlotKind::Citadel(citadel) => citadel,
|
|
_ => continue,
|
|
};
|
|
|
|
let mut null_canvas = NullCanvas;
|
|
let result = structure.render_collect(site, /*canvas*/arena, info, render_area, /*|_canvas| */&mut null_canvas);
|
|
// NOTE: Clearing out the primitives between renders costs us nothing, because any
|
|
// chunks that get deallocated were going to be eventually deallocated anyway, while
|
|
// the current chunk remains for reuse. So this just ends up saving memory.
|
|
arena.reset();
|
|
|
|
/* //println!("{:?}", count_prim_kinds(&result.0));
|
|
iter_fills(canvas, site, plot, result); */
|
|
}
|
|
}
|
|
|
|
/// Null canvas for avoiding writing to the real cnavas.
|
|
struct NullCanvas;
|
|
impl Filler for NullCanvas {
|
|
#[inline]
|
|
fn map<F: Fill>(&mut self, pos: Vec3<i32>, f: F) {
|
|
// black_box(pos);
|
|
// black_box(f);
|
|
// black_box(f.sample_at(pos, Block::empty()));
|
|
black_box((pos, if F::NEEDS_BLOCK {
|
|
f.sample_at(pos, black_box(Block::empty()))
|
|
} else /* if F::NEEDS_POS {
|
|
f.sample_at(pos, Block::empty())
|
|
} else */{
|
|
f.sample_at(pos, Block::empty())
|
|
}));
|
|
}
|
|
|
|
#[inline]
|
|
fn spawn(&mut self, entity: EntityInfo) {
|
|
black_box(entity);
|
|
}
|
|
}
|
|
|
|
/* fn iter_fills(
|
|
canvas: &CanvasInfo<'_>,
|
|
_site: &Site,
|
|
_plot: &Plot,
|
|
(prim_tree, fills, _): (
|
|
Store<Primitive>,
|
|
Vec<(Id<Primitive>, Fill)>,
|
|
Vec<EntityInfo>,
|
|
),
|
|
) {
|
|
let arena = bumpalo::Bump::new();
|
|
let mut cache = HashMap::default();
|
|
|
|
/* let wpos2d = canvas.wpos();
|
|
let chunk_aabr = Aabr {
|
|
min: wpos2d,
|
|
max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
|
|
}; */
|
|
|
|
/* let info = canvas.info(); */
|
|
|
|
for (prim, fill) in fills {
|
|
let aabb = Fill::get_bounds(&mut cache, &prim_tree, prim)/*Aabr::new_empty(Vec2::zero())*/;
|
|
// let mut aabb = Fill::get_bounds(&prim_tree, prim);
|
|
/* fill.sample_at(
|
|
&arena,
|
|
&mut cache,
|
|
&prim_tree,
|
|
prim,
|
|
aabb.into(),
|
|
|pos, f| canvas.map(pos, f)
|
|
); */
|
|
/*Fill::get_bounds_disjoint*/fill.sample_at(&arena, &mut cache, &prim_tree, prim, aabb.into(), canvas, /* |pos| {
|
|
/* canvas.map(pos, |block| {
|
|
let current_block =
|
|
fill.sample_at(
|
|
/* &mut bounds_cache,
|
|
&prim_tree,
|
|
prim, */
|
|
pos,
|
|
&info,
|
|
block,
|
|
);
|
|
/* if let (Some(last_block), None) = (last_block, current_block) {
|
|
spawn(pos, last_block);
|
|
}
|
|
last_block = current_block; */
|
|
current_block.unwrap_or(block)
|
|
}); */
|
|
black_box(/*fill.sample_at(pos, canvas, Block::empty())*/pos);
|
|
}*/&mut NullCanvas);
|
|
|
|
/* for /*mut */aabb in Fill::get_bounds_disjoint(&mut cache, &prim_tree, prim) {
|
|
/* aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z);
|
|
aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z); */
|
|
|
|
for x in aabb.min.x..aabb.max.x {
|
|
for y in aabb.min.y..aabb.max.y {
|
|
for z in aabb.min.z..aabb.max.z {
|
|
/* let col_tile = site.wpos_tile(Vec2::new(x, y));
|
|
if
|
|
/* col_tile.is_building() && */
|
|
col_tile
|
|
.plot()
|
|
.and_then(|p| site.plots().nth(p.id() as usize).unwrap().z_range())
|
|
.zip(plot.z_range())
|
|
.map_or(false, |(a, b)| a.end > b.end)
|
|
{
|
|
continue;
|
|
} */
|
|
let pos = Vec3::new(x, y, z);
|
|
black_box(fill.sample_at(&mut cache, &prim_tree, prim, pos, canvas, Block::empty()));
|
|
}
|
|
}
|
|
}
|
|
} */
|
|
}
|
|
} */
|
|
|
|
fn dungeon(c: &mut Criterion) {
|
|
let pool = ThreadPoolBuilder::new().build().unwrap();
|
|
let (world, index) = World::generate(
|
|
230/*59686*/,
|
|
WorldOpts {
|
|
seed_elements: true,
|
|
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
|
..WorldOpts::default()
|
|
},
|
|
&pool,
|
|
);
|
|
let wpos = Vec2::zero();
|
|
let seed = [1; 32];
|
|
|
|
let air = Block::air(SpriteKind::Empty);
|
|
/* let stone = Block::new(
|
|
BlockKind::Rock,
|
|
zcache_grid
|
|
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
|
.and_then(|zcache| zcache.as_ref())
|
|
.map(|zcache| zcache.sample.stone_col)
|
|
.unwrap_or_else(|| index.colors.deep_stone_color.into()),
|
|
); */
|
|
let water = Block::new(BlockKind::Water, Rgb::zero());
|
|
let mut chunk = TerrainChunk::new(CONFIG.sea_level as i32, water, air, TerrainChunkMeta::void());
|
|
|
|
let find_bounds = |site: &Site| {
|
|
let aabr = site.plots()
|
|
.fold(
|
|
Aabr::new_empty(Vec2::zero()),
|
|
|aabr, plot| {
|
|
// NOTE: Approx
|
|
aabr.union(plot.find_bounds())
|
|
},
|
|
);
|
|
let chunks = Vec2::from(aabr.size())
|
|
.map2(TerrainChunkSize::RECT_SIZE, |e: i32, sz| (e as f32 / sz as f32).ceil() as u32);
|
|
// .as_::<i32>();
|
|
(chunks.x as u64 * chunks.y as u64, aabr)
|
|
};
|
|
|
|
let mut bench_group = |gen_name, render_name, f: fn(&Land, &mut _, _) -> _| {
|
|
c.bench_function(gen_name, |b| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
b.iter(|| {
|
|
f(&Land::from_sim(world.sim()), &mut rng, wpos);
|
|
});
|
|
});
|
|
CanvasInfo::with_mock_canvas_info(index.as_index_ref(), world.sim(), |&info| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
let site = f(&info.land(), &mut rng, wpos);
|
|
let mut render_dungeon_group = c.benchmark_group(render_name);
|
|
let (throughput, render_area) = find_bounds(&site);
|
|
render_dungeon_group.throughput(criterion::Throughput::Elements(throughput));
|
|
render_dungeon_group.bench_function("identity_allocator", |b| {
|
|
b.iter(|| {
|
|
let mut arena = bumpalo::Bump::new();
|
|
/* let mut canvas = Canvas {
|
|
info,
|
|
chunk: &mut chunk,
|
|
arena: &mut arena,
|
|
entities: Vec::new(),
|
|
}; */
|
|
render_plots(/*&mut canvas, */&mut arena, info, &site, render_area);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
bench_group("generate_city", "render_city", Site::generate_city);
|
|
bench_group("generate_cliff_town", "render_cliff_town", Site::generate_cliff_town);
|
|
bench_group("generate_dungeon", "render_dungeon", Site::generate_dungeon);
|
|
bench_group("generate_giant_tree", "render_giant_tree", Site::generate_giant_tree);
|
|
bench_group("generate_gnarling", "render_gnarling", Site::generate_gnarling);
|
|
bench_group("generate_citadel", "render_citadel", Site::generate_citadel);
|
|
|
|
c.bench_function("generate_chunk", |b| {
|
|
// let chunk_pos = (world.sim().map_size_lg().chunks() >> 1).as_();
|
|
// let chunk_pos = Vec2::new(9500 / 32, 29042 / 32);
|
|
// let chunk_pos = Vec2::new(26944 / 32, 26848 / 32);
|
|
// let chunk_pos = Vec2::new(842, 839);
|
|
// let chunk_pos = Vec2::new(24507/32, 20682/32);
|
|
// let chunk_pos = Vec2::new(19638/32, 19621/32);
|
|
// let chunk_pos = Vec2::new(21488/32, 13584/32);
|
|
let chunk_pos = Vec2::new(21488/32 + 5, 13584/32 + 5);
|
|
b.iter(|| {
|
|
black_box(world.generate_chunk(index.as_index_ref(), chunk_pos, || false, None));
|
|
});
|
|
});
|
|
|
|
c.bench_function("deserialize_chunk", |b| {
|
|
// let chunk_pos = (world.sim().map_size_lg().chunks() >> 1).as_();
|
|
// let chunk_pos = Vec2::new(9500 / 32, 29042 / 32);
|
|
// let chunk_pos = Vec2::new(26944 / 32, 26848 / 32);
|
|
// let chunk_pos = Vec2::new(842, 839);
|
|
// let chunk_pos = Vec2::new(24507/32, 20682/32);
|
|
// let chunk_pos = Vec2::new(19638/32, 19621/32);
|
|
// let chunk_pos = Vec2::new(21488/32, 13584/32);
|
|
let chunk_pos = Vec2::new(21488/32 + 5, 13584/32 + 5);
|
|
let chunk = world.generate_chunk(index.as_index_ref(), chunk_pos, || false, None).unwrap().0;
|
|
/* println!("{:?}", chunk.sub_chunks_len());
|
|
let chunk = chunk.sub_chunks().next().unwrap(); */
|
|
let serialized = bincode::serialize(&chunk).unwrap();
|
|
// let chunk_pos = Vec2::new(24507/32, 20682/32);
|
|
// let chunk_pos = Vec2::new(19638/32, 19621/32);
|
|
b.iter(|| {
|
|
black_box(bincode::deserialize::<TerrainChunk/*TerrainSubChunk*//*BlockVec*/>(&*serialized).unwrap());
|
|
});
|
|
});
|
|
|
|
/* c.bench_function("generate_dungeon", |b| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
b.iter(|| {
|
|
Site::generate_dungeon(&Land::empty(), &mut rng, wpos);
|
|
});
|
|
});
|
|
{
|
|
CanvasInfo::with_mock_canvas_info(index.as_index_ref(), world.sim(), |&info| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
let site = Site::generate_dungeon(&info.land(), &mut rng, wpos);
|
|
let mut render_dungeon_group = c.benchmark_group("render_dungeon");
|
|
render_dungeon_group.throughput(criterion::Throughput::Elements(find_bounds(&site)));
|
|
render_dungeon_group.bench_function("identity_allocator", |b| {
|
|
b.iter(|| {
|
|
let mut arena = bumpalo::Bump::new();
|
|
let mut canvas = Canvas {
|
|
info,
|
|
chunk: &mut chunk,
|
|
arena: &mut arena,
|
|
entities: Vec::new(),
|
|
};
|
|
render_plots(&mut canvas, &site);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
c.bench_function("generate_gnarling", |b| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
b.iter(|| {
|
|
Site::generate_gnarling(&Land::empty(), &mut rng, wpos);
|
|
});
|
|
});
|
|
{
|
|
CanvasInfo::with_mock_canvas_info(index.as_index_ref(), world.sim(), |&info| {
|
|
let mut rng = rand::rngs::StdRng::from_seed(seed);
|
|
let site = Site::generate_gnarling(&info.land(), &mut rng, wpos);
|
|
let mut render_gnarling_group = c.benchmark_group("render_gnarling");
|
|
render_gnarling_group.throughput(criterion::Throughput::Elements(find_bounds(&site)));
|
|
render_gnarling_group.bench_function("identity_allocator", |b| {
|
|
b.iter(|| {
|
|
let mut arena = bumpalo::Bump::new();
|
|
let mut canvas = Canvas {
|
|
info,
|
|
chunk: &mut chunk,
|
|
arena: &mut arena,
|
|
entities: Vec::new(),
|
|
};
|
|
render_plots(&mut canvas, &site);
|
|
});
|
|
});
|
|
});
|
|
} */
|
|
}
|
|
|
|
criterion_group!(benches, dungeon);
|
|
criterion_main!(benches);
|