From e47dee388e1e7f051417244ab1c054f8b9acd755 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 25 Feb 2024 22:31:56 -0600 Subject: [PATCH] reduce allocations for collision detection --- Cargo.lock | 1 + azalea-block/src/lib.rs | 2 + azalea-physics/Cargo.toml | 1 + .../src/collision/discrete_voxel_shape.rs | 2 + azalea-physics/src/collision/shape.rs | 71 ++++++++++------ .../src/collision/world_collisions.rs | 23 +++++- azalea/benches/physics.rs | 82 ++++--------------- 7 files changed, 90 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 168ac60bf..6f228d2de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -460,6 +460,7 @@ dependencies = [ "nohash-hasher", "once_cell", "parking_lot", + "smallvec", "tracing", "uuid", ] diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index dc41b4055..2a46fd010 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -59,6 +59,8 @@ impl BlockState { state_id <= Self::max_state() } + /// Returns true if the block is air. This only checks for normal air, not + /// other types like cave air. #[inline] pub fn is_air(&self) -> bool { self == &Self::AIR diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml index 043c0548c..7b7628961 100644 --- a/azalea-physics/Cargo.toml +++ b/azalea-physics/Cargo.toml @@ -21,6 +21,7 @@ tracing = "0.1.40" once_cell = "1.19.0" parking_lot = "^0.12.1" nohash-hasher = "0.2.0" +smallvec = "1.13.1" [dev-dependencies] bevy_time = "0.13.0" diff --git a/azalea-physics/src/collision/discrete_voxel_shape.rs b/azalea-physics/src/collision/discrete_voxel_shape.rs index f63b7c2af..211e63032 100755 --- a/azalea-physics/src/collision/discrete_voxel_shape.rs +++ b/azalea-physics/src/collision/discrete_voxel_shape.rs @@ -13,6 +13,7 @@ pub enum DiscreteVoxelShape { } impl DiscreteVoxelShape { + #[inline] pub fn size(&self, axis: Axis) -> u32 { match self { DiscreteVoxelShape::BitSet(shape) => shape.size(axis), @@ -305,6 +306,7 @@ impl BitSetDiscreteVoxelShape { } impl BitSetDiscreteVoxelShape { + #[inline] fn size(&self, axis: Axis) -> u32 { axis.choose(self.x_size, self.y_size, self.z_size) } diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs index 41ade73cb..56befa393 100755 --- a/azalea-physics/src/collision/shape.rs +++ b/azalea-physics/src/collision/shape.rs @@ -169,22 +169,22 @@ impl Shapes { // var5.getList(), var6.getList(), var7.getList())); let var5 = Self::create_index_merger( 1, - a.get_coords(Axis::X), - b.get_coords(Axis::X), + a.get_coords(Axis::X).to_vec(), + b.get_coords(Axis::X).to_vec(), op_true_false, op_false_true, ); let var6 = Self::create_index_merger( (var5.size() - 1).try_into().unwrap(), - a.get_coords(Axis::Y), - b.get_coords(Axis::Y), + a.get_coords(Axis::Y).to_vec(), + b.get_coords(Axis::Y).to_vec(), op_true_false, op_false_true, ); let var7 = Self::create_index_merger( ((var5.size() - 1) * (var6.size() - 1)).try_into().unwrap(), - a.get_coords(Axis::Z), - b.get_coords(Axis::Z), + a.get_coords(Axis::Z).to_vec(), + b.get_coords(Axis::Z).to_vec(), op_true_false, op_false_true, ); @@ -233,22 +233,22 @@ impl Shapes { let x_merger = Self::create_index_merger( 1, - a.get_coords(Axis::X), - b.get_coords(Axis::X), + a.get_coords(Axis::X).to_vec(), + b.get_coords(Axis::X).to_vec(), op_true_false, op_false_true, ); let y_merger = Self::create_index_merger( (x_merger.size() - 1) as i32, - a.get_coords(Axis::Y), - b.get_coords(Axis::Y), + a.get_coords(Axis::Y).to_vec(), + b.get_coords(Axis::Y).to_vec(), op_true_false, op_false_true, ); let z_merger = Self::create_index_merger( ((x_merger.size() - 1) * (y_merger.size() - 1)) as i32, - a.get_coords(Axis::Z), - b.get_coords(Axis::Z), + a.get_coords(Axis::Z).to_vec(), + b.get_coords(Axis::Z).to_vec(), op_true_false, op_false_true, ); @@ -361,7 +361,7 @@ impl VoxelShape { } } - pub fn get_coords(&self, axis: Axis) -> Vec { + pub fn get_coords(&self, axis: Axis) -> &[f64] { match self { VoxelShape::Array(s) => s.get_coords(axis), VoxelShape::Cube(s) => s.get_coords(axis), @@ -625,6 +625,10 @@ pub struct CubeVoxelShape { // TODO: check where faces is used in minecraft #[allow(dead_code)] faces: Option>, + + x_coords: Vec, + y_coords: Vec, + z_coords: Vec, } impl ArrayVoxelShape { @@ -634,9 +638,9 @@ impl ArrayVoxelShape { let z_size = shape.size(Axis::Z) + 1; // Lengths of point arrays must be consistent with the size of the VoxelShape. - assert_eq!(x_size, xs.len() as u32); - assert_eq!(y_size, ys.len() as u32); - assert_eq!(z_size, zs.len() as u32); + debug_assert_eq!(x_size, xs.len() as u32); + debug_assert_eq!(y_size, ys.len() as u32); + debug_assert_eq!(z_size, zs.len() as u32); Self { faces: None, @@ -648,19 +652,30 @@ impl ArrayVoxelShape { } } -impl CubeVoxelShape { - pub fn new(shape: DiscreteVoxelShape) -> Self { - Self { shape, faces: None } - } -} - impl ArrayVoxelShape { fn shape(&self) -> &DiscreteVoxelShape { &self.shape } - fn get_coords(&self, axis: Axis) -> Vec { - axis.choose(self.xs.clone(), self.ys.clone(), self.zs.clone()) + fn get_coords(&self, axis: Axis) -> &[f64] { + axis.choose(&self.xs, &self.ys, &self.zs) + } +} + +impl CubeVoxelShape { + pub fn new(shape: DiscreteVoxelShape) -> Self { + // pre-calculate the coor + let x_coords = Self::calculate_coords(&shape, Axis::X); + let y_coords = Self::calculate_coords(&shape, Axis::Y); + let z_coords = Self::calculate_coords(&shape, Axis::Z); + + Self { + shape, + faces: None, + x_coords, + y_coords, + z_coords, + } } } @@ -669,8 +684,8 @@ impl CubeVoxelShape { &self.shape } - fn get_coords(&self, axis: Axis) -> Vec { - let size = self.shape.size(axis); + fn calculate_coords(shape: &DiscreteVoxelShape, axis: Axis) -> Vec { + let size = shape.size(axis); let mut parts = Vec::with_capacity(size as usize); for i in 0..=size { parts.push(i as f64 / size as f64); @@ -678,6 +693,10 @@ impl CubeVoxelShape { parts } + fn get_coords(&self, axis: Axis) -> &[f64] { + axis.choose(&self.x_coords, &self.y_coords, &self.z_coords) + } + fn find_index(&self, axis: Axis, coord: f64) -> i32 { let n = self.shape().size(axis); (f64::clamp(coord * (n as f64), -1f64, n as f64)) as i32 diff --git a/azalea-physics/src/collision/world_collisions.rs b/azalea-physics/src/collision/world_collisions.rs index cb721f004..8493b8472 100644 --- a/azalea-physics/src/collision/world_collisions.rs +++ b/azalea-physics/src/collision/world_collisions.rs @@ -22,6 +22,7 @@ pub struct BlockCollisions<'a> { pub only_suffocating_blocks: bool, cached_sections: Vec<(ChunkSectionPos, azalea_world::Section)>, + cached_block_shapes: Vec<(BlockState, &'static VoxelShape)>, } impl<'a> BlockCollisions<'a> { @@ -44,6 +45,7 @@ impl<'a> BlockCollisions<'a> { only_suffocating_blocks: false, cached_sections: Vec::new(), + cached_block_shapes: Vec::new(), } } @@ -95,8 +97,27 @@ impl<'a> BlockCollisions<'a> { self.cached_sections.push((section_pos, section.clone())); + // println!( + // "chunk section length: {}", + // section.states.storage.data.len() + // ); + // println!("biome length: {}", section.biomes.storage.data.len()); + section.get(section_block_pos) } + + fn get_block_shape(&mut self, block_state: BlockState) -> &'static VoxelShape { + for (cached_block_state, cached_shape) in &self.cached_block_shapes { + if block_state == *cached_block_state { + return cached_shape; + } + } + + let shape = block_state.shape(); + self.cached_block_shapes.push((block_state, shape)); + + shape + } } impl<'a> Iterator for BlockCollisions<'a> { @@ -138,7 +159,7 @@ impl<'a> Iterator for BlockCollisions<'a> { )); } - let block_shape = block_state.shape(); + let block_shape = self.get_block_shape(block_state); let block_shape = block_shape.move_relative(item.pos.x as f64, item.pos.y as f64, item.pos.z as f64); diff --git a/azalea/benches/physics.rs b/azalea/benches/physics.rs index 6f8dc7bc2..0d4a3f2fa 100644 --- a/azalea/benches/physics.rs +++ b/azalea/benches/physics.rs @@ -28,29 +28,22 @@ fn generate_world(partial_chunks: &mut PartialChunkStorage, size: u32) -> ChunkS } } - // for chunk_x in -size..size { - // for chunk_z in -size..size { - // let chunk_pos = ChunkPos::new(chunk_x, chunk_z); - // let chunk = chunks.get(&chunk_pos).unwrap(); - // let mut chunk = chunk.write(); - // for x in 0..16_u8 { - // for z in 0..16_u8 { - // chunk.set( - // &ChunkBlockPos::new(x, 1, z), - // azalea_registry::Block::Bedrock.into(), - // chunks.min_y, - // ); - // if rng.gen_bool(0.5) { - // chunk.set( - // &ChunkBlockPos::new(x, 2, z), - // azalea_registry::Block::Bedrock.into(), - // chunks.min_y, - // ); - // } - // } - // } - // } - // } + for chunk_x in -size..size { + for chunk_z in -size..size { + let chunk_pos = ChunkPos::new(chunk_x, chunk_z); + let chunk = chunks.get(&chunk_pos).unwrap(); + let mut chunk = chunk.write(); + for x in 0..16_u8 { + for z in 0..16_u8 { + chunk.set( + &ChunkBlockPos::new(x, 1, z), + azalea_registry::Block::OakFence.into(), + chunks.min_y, + ); + } + } + } + } // let mut start = BlockPos::new(-64, 4, -64); // // move start down until it's on a solid block @@ -69,47 +62,6 @@ fn generate_world(partial_chunks: &mut PartialChunkStorage, size: u32) -> ChunkS chunks } -fn generate_mining_world( - partial_chunks: &mut PartialChunkStorage, - size: u32, -) -> (ChunkStorage, BlockPos, BlockPos) { - let size = size as i32; - - let mut chunks = ChunkStorage::default(); - for chunk_x in -size..size { - for chunk_z in -size..size { - let chunk_pos = ChunkPos::new(chunk_x, chunk_z); - partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks); - } - } - - // let mut rng = StdRng::seed_from_u64(0); - - for chunk_x in -size..size { - for chunk_z in -size..size { - let chunk_pos = ChunkPos::new(chunk_x, chunk_z); - let chunk = chunks.get(&chunk_pos).unwrap(); - let mut chunk = chunk.write(); - for y in chunks.min_y..(chunks.min_y + chunks.height as i32) { - for x in 0..16_u8 { - for z in 0..16_u8 { - chunk.set( - &ChunkBlockPos::new(x, y, z), - azalea_registry::Block::Stone.into(), - chunks.min_y, - ); - } - } - } - } - } - - let start = BlockPos::new(-64, 4, -64); - let end = BlockPos::new(0, 4, 0); - - (chunks, start, end) -} - fn run_physics_benchmark(b: &mut Bencher<'_>) { let mut partial_chunks = PartialChunkStorage::new(32); @@ -126,7 +78,7 @@ fn run_physics_benchmark(b: &mut Bencher<'_>) { // std::process::exit(0); b.iter(|| { - let entity = simulation_set.spawn(SimulatedPlayerBundle::new(Vec3::new(0.0, 4.0, 0.0))); + let entity = simulation_set.spawn(SimulatedPlayerBundle::new(Vec3::new(0.5, 2.0, 0.5))); for _ in 0..20 { simulation_set.tick(); }