From 92f2d36b0c2a1cfbf4cc321fbd43623764a3af71 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 4 Jun 2019 13:45:41 +0100 Subject: [PATCH] Optimised sub-terrain chonk storage, fixed hash chunk bug, altered terrain base --- Cargo.lock | 1 + client/Cargo.toml | 1 + client/src/lib.rs | 14 ++- common/src/terrain/chonk.rs | 163 +++++++++++++++++++++++------------ voxygen/src/scene/terrain.rs | 4 +- world/src/lib.rs | 12 ++- world/src/sim.rs | 6 +- 7 files changed, 141 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79e7642dc8..8bc98412fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2569,6 +2569,7 @@ dependencies = [ name = "veloren-client" version = "0.2.0" dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/client/Cargo.toml b/client/Cargo.toml index 847431764b..fecbfcdd11 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] common = { package = "veloren-common", path = "../common" } +log = "0.4" specs = "0.14" vek = "0.9" threadpool = "1.7" diff --git a/client/src/lib.rs b/client/src/lib.rs index 61f16c482b..ac6dca0ab3 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -12,18 +12,21 @@ use common::{ msg::{ClientMsg, ClientState, ServerInfo, ServerMsg}, net::PostBox, state::State, + terrain::chonk::ChonkMetrics, }; use std::{ collections::HashMap, net::SocketAddr, time::{Duration, Instant}, }; - use threadpool::ThreadPool; +use log::info; use vek::*; const SERVER_TIMEOUT: Duration = Duration::from_secs(20); +const DEBUG_METRICS: bool = true; + pub enum Event { Chat(String), Disconnect, @@ -308,6 +311,15 @@ impl Client { } } + // Output debug metrics + if DEBUG_METRICS && self.tick % 600 == 0 { + let metrics = self.state + .terrain() + .iter() + .fold(ChonkMetrics::default(), |a, (_, c)| a + c.get_metrics()); + info!("{:?}", metrics); + } + // 7) Finish the tick, pass control back to the frontend. self.tick += 1; diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index e5c4de955b..8e546b2fd2 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -4,7 +4,10 @@ use crate::{ volumes::chunk::{Chunk, ChunkErr}, }; use serde_derive::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{ + ops::Add, + collections::HashMap, +}; use vek::*; #[derive(Debug)] @@ -43,6 +46,24 @@ impl Chonk { self.z_offset + (self.sub_chunks.len() as u32 * SUB_CHUNK_HEIGHT) as i32 } + pub fn get_metrics(&self) -> ChonkMetrics { + ChonkMetrics { + chonks: 1, + homogeneous: self.sub_chunks + .iter() + .filter(|s| match s { SubChunk::Homogeneous(_) => true, _ => false }) + .count(), + hash: self.sub_chunks + .iter() + .filter(|s| match s { SubChunk::Hash(_, _) => true, _ => false }) + .count(), + heterogeneous: self.sub_chunks + .iter() + .filter(|s| match s { SubChunk::Heterogeneous(_) => true, _ => false }) + .count(), + } + } + fn sub_chunk_idx(&self, z: i32) -> usize { ((z - self.z_offset) as u32 / SUB_CHUNK_HEIGHT as u32) as usize } @@ -75,7 +96,7 @@ impl ReadVol for Chonk { - Vec3::unit_z() * (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32); - Ok(map.get(&rpos).unwrap_or(cblock)) + Ok(map.get(&rpos.map(|e| e as u8)).unwrap_or(cblock)) } SubChunk::Heterogeneous(chunk) => { let rpos = pos @@ -92,59 +113,61 @@ impl ReadVol for Chonk { impl WriteVol for Chonk { #[inline(always)] fn set(&mut self, pos: Vec3, block: Block) -> Result<(), ChonkError> { - if pos.z < self.z_offset { - Err(ChonkError::OutOfBounds) - } else { - let sub_chunk_idx = self.sub_chunk_idx(pos.z); + while pos.z < self.z_offset { + self.sub_chunks.insert(0, SubChunk::Homogeneous(self.below)); + self.z_offset -= SUB_CHUNK_HEIGHT as i32; + } - while self.sub_chunks.get(sub_chunk_idx).is_none() { - self.sub_chunks.push(SubChunk::Homogeneous(self.above)); - } - - let rpos = pos - - Vec3::unit_z() * (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32); - - match &mut self.sub_chunks[sub_chunk_idx] { - // Can't fail - SubChunk::Homogeneous(cblock) if *cblock == block => Ok(()), - SubChunk::Homogeneous(cblock) => { - let mut map = HashMap::new(); - map.insert(rpos, block); - - self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map); - Ok(()) - } - SubChunk::Hash(cblock, map) if map.len() < 1024 => { - map.insert(rpos, block); - Ok(()) - } - SubChunk::Hash(cblock, map) => { - let mut new_chunk = Chunk::filled(*cblock, ()); - new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope) - - for (map_pos, map_block) in map { - new_chunk.set(*map_pos, *map_block).unwrap(); // Can't fail (I hope!) - } - - self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk); - Ok(()) - } - - /* - SubChunk::Homogeneous(cblock) => { - let mut new_chunk = Chunk::filled(*cblock, ()); - - new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope!) - - self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk); - Ok(()) - } - */ - SubChunk::Heterogeneous(chunk) => chunk - .set(rpos, block) - .map_err(|err| ChonkError::ChunkError(err)), - //_ => unimplemented!(), + let sub_chunk_idx = self.sub_chunk_idx(pos.z); + + while self.sub_chunks.get(sub_chunk_idx).is_none() { + self.sub_chunks.push(SubChunk::Homogeneous(self.above)); + } + + let rpos = pos + - Vec3::unit_z() * (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32); + + match &mut self.sub_chunks[sub_chunk_idx] { + // Can't fail + SubChunk::Homogeneous(cblock) if block == *cblock => Ok(()), + SubChunk::Homogeneous(cblock) => { + let mut map = HashMap::new(); + map.insert(rpos.map(|e| e as u8), block); + + self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map); + Ok(()) + }, + SubChunk::Hash(cblock, map) if block == *cblock => Ok(()), + SubChunk::Hash(cblock, map) if map.len() < 4096 => { + map.insert(rpos.map(|e| e as u8), block); + Ok(()) + }, + SubChunk::Hash(cblock, map) => { + let mut new_chunk = Chunk::filled(*cblock, ()); + new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope) + + for (map_pos, map_block) in map { + new_chunk.set(map_pos.map(|e| e as i32), *map_block).unwrap(); // Can't fail (I hope!) + } + + self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk); + Ok(()) + }, + + /* + SubChunk::Homogeneous(cblock) => { + let mut new_chunk = Chunk::filled(*cblock, ()); + + new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope!) + + self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk); + Ok(()) } + */ + SubChunk::Heterogeneous(chunk) => chunk + .set(rpos, block) + .map_err(|err| ChonkError::ChunkError(err)), + //_ => unimplemented!(), } } } @@ -152,7 +175,7 @@ impl WriteVol for Chonk { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum SubChunk { Homogeneous(Block), - Hash(Block, HashMap, Block>), + Hash(Block, HashMap, Block>), Heterogeneous(Chunk), } @@ -161,3 +184,35 @@ impl SubChunk { SubChunk::Homogeneous(block) } } + +#[derive(Debug)] +pub struct ChonkMetrics { + chonks: usize, + homogeneous: usize, + hash: usize, + heterogeneous: usize, +} + +impl Default for ChonkMetrics { + fn default() -> Self { + ChonkMetrics { + chonks: 0, + homogeneous: 0, + hash: 0, + heterogeneous: 0, + } + } +} + +impl Add for ChonkMetrics { + type Output = Self; + + fn add(self, other: Self::Output) -> Self { + Self::Output { + chonks: self.chonks + other.chonks, + homogeneous: self.homogeneous + other.homogeneous, + hash: self.hash + other.hash, + heterogeneous: self.heterogeneous + other.heterogeneous, + } + } +} diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index da8bd9ec82..9b072e9564 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -151,8 +151,8 @@ impl Terrain { .fold(i32::MIN, |max, (_, chunk)| chunk.get_z_max().max(max)); let aabb = Aabb { - min: Vec3::from(aabr.min) + Vec3::unit_z() * z_min, - max: Vec3::from(aabr.max) + Vec3::unit_z() * z_max, + min: Vec3::from(aabr.min) + Vec3::unit_z() * (z_min - 1), + max: Vec3::from(aabr.max) + Vec3::unit_z() * (z_max + 1), }; // Clone various things so that they can be moved into the thread. diff --git a/world/src/lib.rs b/world/src/lib.rs index 16a3acb68b..86b9ff877a 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -49,7 +49,10 @@ impl World { let warp_nz = BasicMulti::new().set_octaves(3).set_seed(self.sim.seed + 0); - let base_z = match self.sim.get_base_z(chunk_pos.map(|e| e as u32)) { + let base_z = match self.sim + .get(chunk_pos.map(|e| e as u32)) + .map(|chunk| chunk.get_base_z()) + { Some(base_z) => base_z as i32, None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()), }; @@ -64,12 +67,17 @@ impl World { + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); let wposf2d = wpos2d.map(|e| e as f64); + let min_z = self + .sim + .get_interpolated(wpos2d, |chunk| chunk.get_min_z()) + .unwrap_or(0.0) as i32; + let max_z = self .sim .get_interpolated(wpos2d, |chunk| chunk.get_max_z()) .unwrap_or(0.0) as i32; - for z in base_z..max_z.max(sim::SEA_LEVEL as i32) { + for z in min_z..max_z { let lpos = Vec3::new(x, y, z); let wpos = lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); diff --git a/world/src/sim.rs b/world/src/sim.rs index 411a18e02f..d38ed87a93 100644 --- a/world/src/sim.rs +++ b/world/src/sim.rs @@ -746,10 +746,14 @@ impl SimChunk { } pub fn get_base_z(&self) -> f32 { + self.alt - 8.0 + } + + pub fn get_min_z(&self) -> f32 { self.alt - Z_TOLERANCE.0 * (self.chaos + 0.3) } pub fn get_max_z(&self) -> f32 { - self.alt + Z_TOLERANCE.1 + (self.alt + Z_TOLERANCE.1).max(SEA_LEVEL) } }