Optimised sub-terrain chonk storage, fixed hash chunk bug, altered terrain base

This commit is contained in:
Joshua Barretto 2019-06-04 13:45:41 +01:00
parent 910eccee82
commit 92f2d36b0c
7 changed files with 141 additions and 60 deletions

1
Cargo.lock generated
View File

@ -2569,6 +2569,7 @@ dependencies = [
name = "veloren-client" name = "veloren-client"
version = "0.2.0" version = "0.2.0"
dependencies = [ 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)", "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)", "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)", "vek 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
common = { package = "veloren-common", path = "../common" } common = { package = "veloren-common", path = "../common" }
log = "0.4"
specs = "0.14" specs = "0.14"
vek = "0.9" vek = "0.9"
threadpool = "1.7" threadpool = "1.7"

View File

@ -12,18 +12,21 @@ use common::{
msg::{ClientMsg, ClientState, ServerInfo, ServerMsg}, msg::{ClientMsg, ClientState, ServerInfo, ServerMsg},
net::PostBox, net::PostBox,
state::State, state::State,
terrain::chonk::ChonkMetrics,
}; };
use std::{ use std::{
collections::HashMap, collections::HashMap,
net::SocketAddr, net::SocketAddr,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use threadpool::ThreadPool; use threadpool::ThreadPool;
use log::info;
use vek::*; use vek::*;
const SERVER_TIMEOUT: Duration = Duration::from_secs(20); const SERVER_TIMEOUT: Duration = Duration::from_secs(20);
const DEBUG_METRICS: bool = true;
pub enum Event { pub enum Event {
Chat(String), Chat(String),
Disconnect, 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. // 7) Finish the tick, pass control back to the frontend.
self.tick += 1; self.tick += 1;

View File

@ -4,7 +4,10 @@ use crate::{
volumes::chunk::{Chunk, ChunkErr}, volumes::chunk::{Chunk, ChunkErr},
}; };
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap; use std::{
ops::Add,
collections::HashMap,
};
use vek::*; use vek::*;
#[derive(Debug)] #[derive(Debug)]
@ -43,6 +46,24 @@ impl Chonk {
self.z_offset + (self.sub_chunks.len() as u32 * SUB_CHUNK_HEIGHT) as i32 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 { fn sub_chunk_idx(&self, z: i32) -> usize {
((z - self.z_offset) as u32 / SUB_CHUNK_HEIGHT as u32) as 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() - Vec3::unit_z()
* (self.z_offset + sub_chunk_idx as i32 * SUB_CHUNK_HEIGHT as i32); * (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) => { SubChunk::Heterogeneous(chunk) => {
let rpos = pos let rpos = pos
@ -92,9 +113,11 @@ impl ReadVol for Chonk {
impl WriteVol for Chonk { impl WriteVol for Chonk {
#[inline(always)] #[inline(always)]
fn set(&mut self, pos: Vec3<i32>, block: Block) -> Result<(), ChonkError> { fn set(&mut self, pos: Vec3<i32>, block: Block) -> Result<(), ChonkError> {
if pos.z < self.z_offset { while pos.z < self.z_offset {
Err(ChonkError::OutOfBounds) self.sub_chunks.insert(0, SubChunk::Homogeneous(self.below));
} else { self.z_offset -= SUB_CHUNK_HEIGHT as i32;
}
let sub_chunk_idx = self.sub_chunk_idx(pos.z); let sub_chunk_idx = self.sub_chunk_idx(pos.z);
while self.sub_chunks.get(sub_chunk_idx).is_none() { while self.sub_chunks.get(sub_chunk_idx).is_none() {
@ -106,29 +129,30 @@ impl WriteVol for Chonk {
match &mut self.sub_chunks[sub_chunk_idx] { match &mut self.sub_chunks[sub_chunk_idx] {
// Can't fail // Can't fail
SubChunk::Homogeneous(cblock) if *cblock == block => Ok(()), SubChunk::Homogeneous(cblock) if block == *cblock => Ok(()),
SubChunk::Homogeneous(cblock) => { SubChunk::Homogeneous(cblock) => {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert(rpos, block); map.insert(rpos.map(|e| e as u8), block);
self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map); self.sub_chunks[sub_chunk_idx] = SubChunk::Hash(*cblock, map);
Ok(()) Ok(())
} },
SubChunk::Hash(cblock, map) if map.len() < 1024 => { SubChunk::Hash(cblock, map) if block == *cblock => Ok(()),
map.insert(rpos, block); SubChunk::Hash(cblock, map) if map.len() < 4096 => {
map.insert(rpos.map(|e| e as u8), block);
Ok(()) Ok(())
} },
SubChunk::Hash(cblock, map) => { SubChunk::Hash(cblock, map) => {
let mut new_chunk = Chunk::filled(*cblock, ()); let mut new_chunk = Chunk::filled(*cblock, ());
new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope) new_chunk.set(rpos, block).unwrap(); // Can't fail (I hope)
for (map_pos, map_block) in map { for (map_pos, map_block) in map {
new_chunk.set(*map_pos, *map_block).unwrap(); // Can't fail (I hope!) 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); self.sub_chunks[sub_chunk_idx] = SubChunk::Heterogeneous(new_chunk);
Ok(()) Ok(())
} },
/* /*
SubChunk::Homogeneous(cblock) => { SubChunk::Homogeneous(cblock) => {
@ -147,12 +171,11 @@ impl WriteVol for Chonk {
} }
} }
} }
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SubChunk { pub enum SubChunk {
Homogeneous(Block), Homogeneous(Block),
Hash(Block, HashMap<Vec3<i32>, Block>), Hash(Block, HashMap<Vec3<u8>, Block>),
Heterogeneous(Chunk<Block, TerrainChunkSize, ()>), Heterogeneous(Chunk<Block, TerrainChunkSize, ()>),
} }
@ -161,3 +184,35 @@ impl SubChunk {
SubChunk::Homogeneous(block) 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,
}
}
}

View File

@ -151,8 +151,8 @@ impl Terrain {
.fold(i32::MIN, |max, (_, chunk)| chunk.get_z_max().max(max)); .fold(i32::MIN, |max, (_, chunk)| chunk.get_z_max().max(max));
let aabb = Aabb { let aabb = Aabb {
min: Vec3::from(aabr.min) + Vec3::unit_z() * z_min, min: Vec3::from(aabr.min) + Vec3::unit_z() * (z_min - 1),
max: Vec3::from(aabr.max) + Vec3::unit_z() * z_max, max: Vec3::from(aabr.max) + Vec3::unit_z() * (z_max + 1),
}; };
// Clone various things so that they can be moved into the thread. // Clone various things so that they can be moved into the thread.

View File

@ -49,7 +49,10 @@ impl World {
let warp_nz = BasicMulti::new().set_octaves(3).set_seed(self.sim.seed + 0); 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, Some(base_z) => base_z as i32,
None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()), 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); + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
let wposf2d = wpos2d.map(|e| e as f64); 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 let max_z = self
.sim .sim
.get_interpolated(wpos2d, |chunk| chunk.get_max_z()) .get_interpolated(wpos2d, |chunk| chunk.get_max_z())
.unwrap_or(0.0) as i32; .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 lpos = Vec3::new(x, y, z);
let wpos = let wpos =
lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);

View File

@ -746,10 +746,14 @@ impl SimChunk {
} }
pub fn get_base_z(&self) -> f32 { 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) self.alt - Z_TOLERANCE.0 * (self.chaos + 0.3)
} }
pub fn get_max_z(&self) -> f32 { pub fn get_max_z(&self) -> f32 {
self.alt + Z_TOLERANCE.1 (self.alt + Z_TOLERANCE.1).max(SEA_LEVEL)
} }
} }