From 5286d8892d579deabb5e4f562de3cb3e5c2f44db Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 22 Nov 2024 19:06:48 -0500 Subject: [PATCH] some chunk gen optimizations --- pumpkin-world/src/block/block_state.rs | 18 ++++++------ pumpkin-world/src/lib.rs | 13 ++++----- pumpkin-world/src/world_gen/chunk_noise.rs | 7 +++++ .../src/world_gen/implementation/test.rs | 20 +++++++++---- pumpkin-world/src/world_gen/positions.rs | 4 +++ pumpkin-world/src/world_gen/proto_chunk.rs | 15 ++++------ pumpkin-world/src/world_gen/sampler.rs | 28 +++++++++++-------- 7 files changed, 62 insertions(+), 43 deletions(-) diff --git a/pumpkin-world/src/block/block_state.rs b/pumpkin-world/src/block/block_state.rs index 4237e2ae4..a23001200 100644 --- a/pumpkin-world/src/block/block_state.rs +++ b/pumpkin-world/src/block/block_state.rs @@ -1,18 +1,23 @@ -use super::block_registry::{get_block, get_block_and_state_by_state_id}; +use super::block_registry::{get_block, get_state_by_state_id}; #[derive(Clone, Copy, Debug)] pub struct BlockState { pub state_id: u16, + pub block_id: u16, } impl BlockState { - pub const AIR: BlockState = BlockState { state_id: 0 }; + pub const AIR: BlockState = BlockState { + state_id: 0, + block_id: 0, + }; /// Get a Block from the Vanilla Block registry at Runtime pub fn new(registry_id: &str) -> Option { let block = get_block(registry_id); block.map(|block| Self { state_id: block.default_state_id, + block_id: block.id, }) } @@ -22,15 +27,12 @@ impl BlockState { #[inline] pub fn is_air(&self) -> bool { - let (_, state) = get_block_and_state_by_state_id(self.state_id).unwrap(); - state.air + get_state_by_state_id(self.state_id).unwrap().air } #[inline] - pub fn of_block(&self, block_id: &str) -> bool { - let (this_block, _) = get_block_and_state_by_state_id(self.state_id).unwrap(); - let check_block = get_block(block_id).unwrap(); - this_block.id == check_block.id + pub fn of_block(&self, block_id: u16) -> bool { + self.block_id == block_id } } diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index e91a5033f..03f13e53a 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,6 +1,6 @@ use block::BlockState; use world_gen::{ - chunk_noise::ChunkNoiseGenerator, + chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK}, generation_shapes::GenerationShape, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, proto_chunk::StandardChunkFluidLevelSampler, @@ -22,16 +22,13 @@ pub const WORLD_LOWEST_Y: i16 = -64; pub const WORLD_MAX_Y: i16 = WORLD_HEIGHT as i16 - WORLD_LOWEST_Y.abs(); pub const DIRECT_PALETTE_BITS: u32 = 15; -const LAVA: &str = "minecraft:lava"; -const WATER: &str = "minecraft:water"; - // TODO: is there a way to do in-file benches? pub fn bench_create_chunk_noise_overworld() { let config = NoiseConfig::new(0, &OVERWORLD_NOISE_ROUTER); let generation_shape = GenerationShape::SURFACE; let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler { - bottom_fluid: FluidLevel::new(-54, BlockState::new(LAVA).unwrap()), - top_fluid: FluidLevel::new(62, BlockState::new(WATER).unwrap()), + bottom_fluid: FluidLevel::new(-54, *LAVA_BLOCK), + top_fluid: FluidLevel::new(62, *WATER_BLOCK), }); ChunkNoiseGenerator::new( @@ -49,8 +46,8 @@ pub fn bench_create_chunk_noise_overworld() { pub fn bench_create_and_populate_noise() { let config = NoiseConfig::new(0, &OVERWORLD_NOISE_ROUTER); let fluid_sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler { - bottom_fluid: FluidLevel::new(-54, BlockState::new(LAVA).unwrap()), - top_fluid: FluidLevel::new(62, BlockState::new(WATER).unwrap()), + bottom_fluid: FluidLevel::new(-54, *LAVA_BLOCK), + top_fluid: FluidLevel::new(62, *WATER_BLOCK), }); let generation_shape = GenerationShape::SURFACE; diff --git a/pumpkin-world/src/world_gen/chunk_noise.rs b/pumpkin-world/src/world_gen/chunk_noise.rs index 2bd08d278..06f30f3cb 100644 --- a/pumpkin-world/src/world_gen/chunk_noise.rs +++ b/pumpkin-world/src/world_gen/chunk_noise.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, hash::Hash, mem, num::Wrapping, ops::AddAssign, sync::Arc}; +use lazy_static::lazy_static; use num_traits::Zero; use parking_lot::Mutex; use pumpkin_core::math::{floor_div, vector2::Vector2, vector3::Vector3}; @@ -40,6 +41,12 @@ use super::{ }, }; +lazy_static! { + pub static ref STONE_BLOCK: BlockState = BlockState::new("minecraft:stone").unwrap(); + pub static ref LAVA_BLOCK: BlockState = BlockState::new("minecraft:lava").unwrap(); + pub static ref WATER_BLOCK: BlockState = BlockState::new("minecraft:water").unwrap(); +} + pub struct ChunkCacheOnceFunction> { delegate: R, sample_unique_index: u64, diff --git a/pumpkin-world/src/world_gen/implementation/test.rs b/pumpkin-world/src/world_gen/implementation/test.rs index 12f0a2f95..5be92446a 100644 --- a/pumpkin-world/src/world_gen/implementation/test.rs +++ b/pumpkin-world/src/world_gen/implementation/test.rs @@ -1,4 +1,7 @@ +use std::{num::Wrapping, ops::SubAssign}; + use dashmap::{DashMap, Entry}; +use num_traits::Zero; use pumpkin_core::math::{vector2::Vector2, vector3::Vector3}; use crate::{ @@ -91,7 +94,7 @@ impl BiomeGenerator for TestBiomeGenerator { } pub(crate) struct TestTerrainGenerator { - chunks: DashMap, ProtoChunk>, + chunks: DashMap, (ProtoChunk, Wrapping)>, } impl GeneratorInit for TestTerrainGenerator { @@ -111,12 +114,19 @@ impl TerrainGenerator for TestTerrainGenerator { //println!("Populating chunk: {:?}", at); proto_chunk.populate_noise(); //println!("Done populating chunk: {:?} ({:?})", at, inst.elapsed()); - entry.insert(proto_chunk); + entry.insert((proto_chunk, Wrapping(1))); } } fn clean_chunk(&self, at: &Vector2) { - self.chunks.remove(at); + let entry = self.chunks.entry(*at); + if let Entry::Occupied(mut entry) = entry { + let (_, count) = entry.get_mut(); + count.sub_assign(1); + if count.is_zero() { + entry.remove(); + } + } } // TODO allow specifying which blocks should be at which height in the config. @@ -126,8 +136,8 @@ impl TerrainGenerator for TestTerrainGenerator { local_pos: Vector3, _: Biome, ) -> BlockState { - if let Some(chunk) = self.chunks.get(chunk_pos) { - chunk.get_block_state(&local_pos) + if let Some(entry) = self.chunks.get(chunk_pos) { + entry.0.get_block_state(&local_pos) } else { panic!("Chunk needs to exist") } diff --git a/pumpkin-world/src/world_gen/positions.rs b/pumpkin-world/src/world_gen/positions.rs index 64514632c..6fc4f33a5 100644 --- a/pumpkin-world/src/world_gen/positions.rs +++ b/pumpkin-world/src/world_gen/positions.rs @@ -7,18 +7,22 @@ pub mod block_pos { BITS_X, BITS_Y, BITS_Z, BIT_SHIFT_X, BIT_SHIFT_Z, SIZE_BITS_X, SIZE_BITS_Y, SIZE_BITS_Z, }; + #[inline] pub const fn unpack_x(packed: u64) -> i32 { (((packed as i64) << (64 - BIT_SHIFT_X - SIZE_BITS_X)) >> (64 - SIZE_BITS_X)) as i32 } + #[inline] pub const fn unpack_y(packed: u64) -> i32 { (((packed as i64) << (64 - SIZE_BITS_Y)) >> (64 - SIZE_BITS_Y)) as i32 } + #[inline] pub const fn unpack_z(packed: u64) -> i32 { (((packed as i64) << (64 - BIT_SHIFT_Z - SIZE_BITS_Z)) >> (64 - SIZE_BITS_Z)) as i32 } + #[inline] pub const fn packed(vec: &Vector3) -> u64 { let mut result = 0i64; // Need to go to i64 first to conserve sign diff --git a/pumpkin-world/src/world_gen/proto_chunk.rs b/pumpkin-world/src/world_gen/proto_chunk.rs index 33d39b41f..407686a28 100644 --- a/pumpkin-world/src/world_gen/proto_chunk.rs +++ b/pumpkin-world/src/world_gen/proto_chunk.rs @@ -13,7 +13,7 @@ use crate::{ }; use super::{ - chunk_noise::ChunkNoiseGenerator, + chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, STONE_BLOCK, WATER_BLOCK}, positions::chunk_pos::{start_block_x, start_block_z}, sampler::{FluidLevel, FluidLevelSamplerImpl}, }; @@ -45,9 +45,6 @@ pub struct ProtoChunk { // may want to use chunk status } -const LAVA: &str = "minecraft:lava"; -const WATER: &str = "minecraft:water"; - impl ProtoChunk { pub fn new(chunk_pos: Vector2) -> Self { // TODO: Don't hardcode these @@ -61,8 +58,8 @@ impl ProtoChunk { // TODO: Customize these let sampler = FluidLevelSampler::Chunk(StandardChunkFluidLevelSampler { - bottom_fluid: FluidLevel::new(-54, BlockState::new(LAVA).unwrap()), - top_fluid: FluidLevel::new(62, BlockState::new(WATER).unwrap()), + bottom_fluid: FluidLevel::new(-54, *LAVA_BLOCK), + top_fluid: FluidLevel::new(62, *WATER_BLOCK), }); let sampler = ChunkNoiseGenerator::new( @@ -152,10 +149,8 @@ impl ProtoChunk { self.sampler.interpolate_z(block_z, delta_z); // TODO: Change default block - let block_state = self - .sampler - .sample_block_state() - .unwrap_or(BlockState::new("minecraft:stone").unwrap()); + let block_state = + self.sampler.sample_block_state().unwrap_or(*STONE_BLOCK); //log::debug!("Sampled block state in {:?}", inst.elapsed()); let local_pos = Vector3 { diff --git a/pumpkin-world/src/world_gen/sampler.rs b/pumpkin-world/src/world_gen/sampler.rs index a3f2a5538..479a41772 100644 --- a/pumpkin-world/src/world_gen/sampler.rs +++ b/pumpkin-world/src/world_gen/sampler.rs @@ -9,7 +9,7 @@ use pumpkin_core::{ use crate::block::BlockState; use super::{ - chunk_noise::{ChunkNoiseDensityFunctions, ChunkNoiseState}, + chunk_noise::{ChunkNoiseDensityFunctions, ChunkNoiseState, LAVA_BLOCK, WATER_BLOCK}, noise::{ clamped_map, density::{ @@ -22,9 +22,6 @@ use super::{ section_coords, }; -const LAVA: &str = "minecraft:lava"; -const WATER: &str = "minecraft:water"; - pub enum BlockStateSampler { Aquifer(AquiferSampler), Ore(OreVeinSampler), @@ -211,6 +208,7 @@ impl WorldAquiferSampler { } } + #[inline] fn index(&self, x: i32, y: i32, z: i32) -> usize { let i = (x - self.start_x) as usize; let j = (y - self.start_y as i32) as usize; @@ -219,6 +217,7 @@ impl WorldAquiferSampler { (j * self.size_z + k) * self.size_x + i } + #[inline] fn max_distance(i: i32, a: i32) -> f64 { 1f64 - ((a - i).abs() as f64) / 25f64 } @@ -235,8 +234,10 @@ impl WorldAquiferSampler { let block_state1 = level_1.get_block_state(y); let block_state2 = level_2.get_block_state(y); - if (!block_state1.of_block(LAVA) || !block_state2.of_block(WATER)) - && (!block_state1.of_block(WATER) || !block_state2.of_block(LAVA)) + if (!block_state1.of_block(LAVA_BLOCK.block_id) + || !block_state2.of_block(WATER_BLOCK.block_id)) + && (!block_state1.of_block(WATER_BLOCK.block_id) + || !block_state2.of_block(LAVA_BLOCK.block_id)) { let level_diff = (level_1.max_y - level_2.max_y).abs(); if level_diff == 0 { @@ -446,7 +447,10 @@ impl WorldAquiferSampler { level: i32, env: &ChunkNoiseState, ) -> BlockState { - if level <= -10 && level != MIN_HEIGHT_CELL && !default_level.state.of_block(LAVA) { + if level <= -10 + && level != MIN_HEIGHT_CELL + && !default_level.state.of_block(LAVA_BLOCK.block_id) + { let x = floor_div(block_x, 64); let y = floor_div(block_y, 40); let z = floor_div(block_z, 64); @@ -456,7 +460,7 @@ impl WorldAquiferSampler { .sample_mut(&NoisePos::Unblended(UnblendedNoisePos::new(x, y, z)), env); if sample.abs() > 0.3f64 { - return BlockState::new(LAVA).unwrap(); + return *LAVA_BLOCK; } } @@ -504,8 +508,8 @@ impl AquiferSamplerImpl for WorldAquiferSampler { let k = pos.z(); let fluid_level = self.fluid_level.get_fluid_level(i, j, k); - if fluid_level.get_block_state(j).of_block(LAVA) { - Some(BlockState::new(LAVA).unwrap()) + if fluid_level.get_block_state(j).of_block(LAVA_BLOCK.block_id) { + Some(*LAVA_BLOCK) } else { let l = floor_div(i - 5, 16); let m = floor_div(j + 1, 12); @@ -586,12 +590,12 @@ impl AquiferSamplerImpl for WorldAquiferSampler { if d <= 0f64 { // TODO: Handle fluid tick Some(block_state) - } else if block_state.of_block(WATER) + } else if block_state.of_block(WATER_BLOCK.block_id) && self .fluid_level .get_fluid_level(i, j - 1, k) .get_block_state(j - 1) - .of_block(LAVA) + .of_block(LAVA_BLOCK.block_id) { Some(block_state) } else {