From 7039d5a29ce451d198526973ed1e73ea41287e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 14 Oct 2019 15:22:35 +0200 Subject: [PATCH] move away from the system that a layer stores it's parent index, but let a layer store their index used to access the child data. which makes everything much clearer and nicer to read and easier to develop on Redone the whole implementation again on this and now use a trait for HashResult and VecResult which solves the .get() problem and .mat() problem added benchmarks and (with repeated tests) we gain 14ns for 1 hash lookup and 2 veclookups and the result vec lookup --- Cargo.lock | 1 + worldsim/Cargo.toml | 1 + worldsim/src/lodstore/area.rs | 12 +- worldsim/src/lodstore/data.rs | 38 +- worldsim/src/lodstore/delta.rs | 4 +- worldsim/src/lodstore/index.rs | 259 +------------- worldsim/src/lodstore/lodpos.rs | 292 +++++++++++++++ worldsim/src/lodstore/mod.rs | 3 +- worldsim/src/lodstore/newdata.rs | 549 ++++++++++++++--------------- worldsim/src/region/lod/example.rs | 27 +- worldsim/src/region/lod/npcs.rs | 3 +- worldsim/src/region/lod/terrain.rs | 5 +- 12 files changed, 632 insertions(+), 562 deletions(-) create mode 100644 worldsim/src/lodstore/lodpos.rs diff --git a/Cargo.lock b/Cargo.lock index c6bccbc9c3..b19568532a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6648,6 +6648,7 @@ version = "0.1.0" dependencies = [ "bincode", "dot_vox", + "fxhash", "log", "mio 0.6.23", "mio-extras", diff --git a/worldsim/Cargo.toml b/worldsim/Cargo.toml index baffcc9aea..b5e93ab7c4 100644 --- a/worldsim/Cargo.toml +++ b/worldsim/Cargo.toml @@ -19,3 +19,4 @@ serde_derive = "1.0" bincode = "1.0" log = "0.4" rand = "0.5" +fxhash = "0.2" diff --git a/worldsim/src/lodstore/area.rs b/worldsim/src/lodstore/area.rs index 75bb8a5ace..009a96c06e 100644 --- a/worldsim/src/lodstore/area.rs +++ b/worldsim/src/lodstore/area.rs @@ -1,5 +1,5 @@ -use super::index::{ - LodIndex, +use super::lodpos::{ + LodPos, }; /* @@ -8,19 +8,19 @@ use super::index::{ #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub struct LodArea { - pub lower: LodIndex, - pub upper: LodIndex, + pub lower: LodPos, + pub upper: LodPos, } impl LodArea { - pub fn new(lower: LodIndex, upper: LodIndex) -> Self { + pub fn new(lower: LodPos, upper: LodPos) -> Self { LodArea { lower, upper, } } - pub fn is_inside(&self, lod: LodIndex) -> bool { + pub fn is_inside(&self, lod: LodPos) -> bool { let lower = self.lower.get(); let upper = self.upper.get(); let lod = lod.get(); diff --git a/worldsim/src/lodstore/data.rs b/worldsim/src/lodstore/data.rs index 8e839f884f..5e886dfe80 100644 --- a/worldsim/src/lodstore/data.rs +++ b/worldsim/src/lodstore/data.rs @@ -1,9 +1,9 @@ use std::u32; use std::collections::HashMap; use vek::*; -use super::index::{ +use super::lodpos::{ self, - LodIndex, + LodPos, AbsIndex, relative_to_1d, two_pow_u, @@ -15,6 +15,8 @@ use super::delta::{ LodDelta, }; +pub type LodIndex = LodPos; + /* LOD Data contains different Entries in different Vecs, every entry has a "pointer" to it's child start. This is the structure to store a region and all subscribed information @@ -228,7 +230,7 @@ impl LodData // uses parent_child_index as a buffer, to not calculate it again fn int_get(parent_abs: AbsIndex, child_lod: LodIndex, parent_lod: LodIndex, parent_child_index: usize) -> AbsIndex { let child_layer = X::child_layer_id[parent_abs.layer as usize].unwrap(); - let child_lod = child_lod.align_to_layer_id(child_layer); + let child_lod = child_lod.align_to_level(child_layer); let child_offset = relative_to_1d(child_lod, parent_lod, child_layer, X::layer_volume[child_layer as usize]); //println!("{} int_get - parent_abs {} child_lod {} parent_lod {} parent_child_index {} child_offset {}", Self::debug_offset(parent_abs.layer), parent_abs, child_lod, parent_lod, parent_child_index, child_offset); AbsIndex::new(child_layer, parent_child_index + child_offset) @@ -236,7 +238,7 @@ impl LodData // slower variant of int_get which requiere self lookups fn int_get_lockup(&self, parent_abs: AbsIndex, child_lod: LodIndex) -> AbsIndex { - let parent_lod = child_lod.align_to_layer_id(parent_abs.layer); + let parent_lod = child_lod.align_to_level(parent_abs.layer); let parent_child_index = self.int_get_child_index(parent_abs); Self::int_get(parent_abs, child_lod, parent_lod, parent_child_index) } @@ -255,7 +257,7 @@ impl LodData } pub fn int_get_n(&self, index: LodIndex, layer: u8) -> AbsIndex { - let anchor_lod = index.align_to_layer_id(X::anchor_layer_id); + let anchor_lod = index.align_to_level(X::anchor_layer_id); let anchor_abs = AbsIndex::new(X::anchor_layer_id, self.anchor[&anchor_lod]); let wanted_abs = self.int_recursive_get(anchor_abs, index, layer); debug_assert_eq!(wanted_abs.layer, layer); @@ -272,10 +274,10 @@ impl LodData parent_abs = self.int_get_lockup(parent_abs, child_lod); if parent_abs.layer <= target_layer { let parent_width = two_pow_u(old_parent_abs.layer ) as u32; - let parent_lod = child_lod.align_to_layer_id(old_parent_abs.layer); + let parent_lod = child_lod.align_to_level(old_parent_abs.layer); //TODO: Dont recalc the first 3 values cache.last_parent_area = LodArea::new(parent_lod, parent_lod + LodIndex::new(Vec3::new(parent_width,parent_width,parent_width))); - cache.last_parent_lod = child_lod.align_to_layer_id(old_parent_abs.layer); + cache.last_parent_lod = child_lod.align_to_level(old_parent_abs.layer); cache.last_parent_child_index = self.int_get_child_index(old_parent_abs); cache.last_parent_abs = old_parent_abs; return parent_abs; @@ -292,7 +294,7 @@ impl LodData //println!("nay"); //println!("{} {}", cache.last_parent_area.lower, cache.last_parent_area.upper); //println!("{}", index); - let anchor_lod = index.align_to_layer_id(X::anchor_layer_id); + let anchor_lod = index.align_to_level(X::anchor_layer_id); let anchor_abs = AbsIndex::new(X::anchor_layer_id, self.anchor[&anchor_lod]); self.int_recursive_get_cached(cache, anchor_abs, index, layer) }; @@ -472,7 +474,7 @@ impl LodData fn int_make_at_least(&mut self, parent: AbsIndex, /*parent_lod2: LodIndex,*/ area: LodArea, target_layer: u8, delta: &Option<&mut LodDelta>) { let child_layer = X::child_layer_id[parent.layer as usize]; let parent_lod_width = two_pow_u(parent.layer) as u32; - let parent_lod = area.lower.align_to_layer_id(parent.layer); + let parent_lod = area.lower.align_to_level(parent.layer); //assert_eq!(parent_lod, parent_lod2); //println!("{} lower, upper {} {} {} - {:?}", Self::debug_offset(parent.layer), area.lower, area.upper, parent_lod_width, child_layer); //let delta = delta.unwrap(); @@ -497,8 +499,8 @@ impl LodData let child_layer = child_layer.unwrap(); let child_lod_width = two_pow_u(child_layer) as u32; //calc childs which needs to be called recusivly, there childs will be the new parents - let child_lower = area.lower.align_to_layer_id(child_layer); - let child_upper = area.upper.align_to_layer_id(child_layer); + let child_lower = area.lower.align_to_level(child_layer); + let child_upper = area.upper.align_to_level(child_layer); let child_base_abs_index = self.int_get_child_index(parent); let child_volume = X::layer_volume[child_layer as usize]; // loop over childs and calculate correct lower and @@ -517,8 +519,8 @@ impl LodData let child_lower = parent_lod + LodIndex::new(Vec3::new(x * child_lod_width, y * child_lod_width, z * child_lod_width)); let child_upper = child_lower + LodIndex::new(Vec3::new(child_lod_width-1, child_lod_width-1, child_lod_width-1)); - let inner_lower = index::max(area.lower, child_lower); - let inner_upper = index::min(area.upper, child_upper); + let inner_lower = lodpos::max(area.lower, child_lower); + let inner_upper = lodpos::min(area.upper, child_upper); //println!("{} restrict {} {} to {} {}", Self::debug_offset(parent.layer), area.lower, area.upper, inner_lower, inner_upper); let inner_area = LodArea::new(inner_lower, inner_upper); Self::int_make_at_least(self, child_abs, inner_area, target_layer, delta); @@ -536,11 +538,11 @@ impl LodData pub fn make_at_least(&mut self, area: LodArea, target_layer: u8, delta: Option<&mut LodDelta>) { let anchor_layer_id = X::anchor_layer_id; - let anchor_lower = area.lower.align_to_layer_id(anchor_layer_id); - let anchor_upper = area.upper.align_to_layer_id(anchor_layer_id); + let anchor_lower = area.lower.align_to_level(anchor_layer_id); + let anchor_upper = area.upper.align_to_level(anchor_layer_id); let lower_xyz = anchor_lower.get(); let upper_xyz = anchor_upper.get(); - let anchor_width = index::two_pow_u(anchor_layer_id) as u32; + let anchor_width = lodpos::two_pow_u(anchor_layer_id) as u32; let mut x = lower_xyz[0]; //println!("{} xxx lower, upper {} {} {}", Self::debug_offset(anchor_layer_id), lower_xyz, upper_xyz, anchor_width); while x <= upper_xyz[0] { @@ -553,8 +555,8 @@ impl LodData if anchor_abs.layer > target_layer { let child_lod_upper = Self::get_last_child_lod(anchor_lod, anchor_abs.layer); - let inner_lower = index::max(area.lower, anchor_lod); - let inner_upper = index::min(area.upper, child_lod_upper); + let inner_lower = lodpos::max(area.lower, anchor_lod); + let inner_upper = lodpos::min(area.upper, child_lod_upper); //println!("{}call child with lower, upper {} {} instead of {} {} ", Self::debug_offset(anchor_layer_id), inner_lower, inner_upper, anchor_lod, child_lod_upper); let inner_area = LodArea::new(inner_lower, inner_upper); diff --git a/worldsim/src/lodstore/delta.rs b/worldsim/src/lodstore/delta.rs index 09171280bd..b420998f25 100644 --- a/worldsim/src/lodstore/delta.rs +++ b/worldsim/src/lodstore/delta.rs @@ -3,10 +3,12 @@ use super::{ LodData, LodConfig, }, - index::LodIndex, + lodpos::LodPos, area::LodArea, }; +pub type LodIndex = LodPos; + /* A LodDelta applies a change to a Lod The rules for LodDeltas are strict in order to make them as simple as possible. diff --git a/worldsim/src/lodstore/index.rs b/worldsim/src/lodstore/index.rs index 3bc3ecd4d8..8db21650a6 100644 --- a/worldsim/src/lodstore/index.rs +++ b/worldsim/src/lodstore/index.rs @@ -3,256 +3,27 @@ use std::ops::Sub; use std::ops::Add; use std::cmp; use std::fmt; +use std::{u16, u32}; -/* -A region owns the Values from in (0, 2048) in steps of 1/32. -But because regions can also subscribe we add support to the range (0, 2048*3). -which is 13 bits for the digits before the decimal point and 5 bits for the digits after the decimal point. -We use our own LodIndex type to store and compute based on these values, because u16 arithmetic (inside the owned area) is super easy to archive and allows us to optimize a lot. - - --- lower neighbor -0 -> 0 -65535 -> 2047 31/32 --- owned -65536 -> 2048 -131071 -> 4095 31/32 --- upper neighbor -196607 -> 6143 31/32 -*/ - -#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] -pub struct LodIndex { - /* - bit 0..17 -> x - bit 18..35 -> y - bit 36..53 -> z - bit 54..63 -> unused - */ - data: u64, +pub trait ToOptionUsize: Copy { + fn is_some(self) -> bool; + fn into_usize(self) -> usize; } -/*does not work on big endian!*/ -const BIT_X_MASK: u64 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011_1111_1111_1111_1111; -const BIT_Y_MASK: u64 = 0b0000_0000_0000_0000_0000_0000_0000_1111_1111_1111_1111_1100_0000_0000_0000_0000; -const BIT_Z_MASK: u64 = 0b0000_0000_0011_1111_1111_1111_1111_0000_0000_0000_0000_0000_0000_0000_0000_0000; -const BIT_X_MASK32: u32 = 0b0000_0000_0000_0011_1111_1111_1111_1111; - -//TODO: Optimize! -impl LodIndex { - pub fn new(data: Vec3) -> Self { - let mut index = LodIndex {data: 0}; - index.set(data); - index +impl ToOptionUsize for u32 { + fn is_some(self) -> bool { + self != u32::MAX } - - pub fn get(&self) -> Vec3 { - let x = (self.data & BIT_X_MASK) as u32; - let y = ((self.data & BIT_Y_MASK) >> 18 ) as u32; - let z = ((self.data & BIT_Z_MASK) >> 36 ) as u32; - Vec3{x,y,z} - } - - pub fn set(&mut self, data: Vec3) { - let x = (data.x & BIT_X_MASK32) as u64; - let y = ((data.y & BIT_X_MASK32) as u64 ) << 18; - let z = ((data.z & BIT_X_MASK32) as u64 ) << 36; - self.data = x + y + z; - } - - pub fn align_to_layer_id(&self, layer: u8) -> LodIndex { - let xyz = self.get(); - let f = two_pow_u(layer) as u32; - LodIndex::new(xyz.map(|i| { - (i / f) * f - })) - } - - pub fn get_highest_layer_that_fits(&self) -> u8 { - let pos = self.get(); - cmp::min( cmp::min(cmp::min(pos[0].trailing_zeros(), - pos[1].trailing_zeros()), pos[2].trailing_zeros()), 15) as u8 + fn into_usize(self) -> usize { + self as usize } } -impl fmt::Display for LodIndex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let xyz = self.get(); - //write!(f, "({}|{}|{}) <{}>", xyz[0], xyz[1], xyz[2], self.data) - write!(f, "({}|{}|{})", xyz[0], xyz[1], xyz[2]) +impl ToOptionUsize for u16 { + fn is_some(self) -> bool { + self != u16::MAX } -} - -#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] -pub struct AbsIndex { - pub layer: u8, - pub index: usize, -} - -impl AbsIndex { - pub fn new(layer: u8, index: usize) -> Self { - AbsIndex { - layer, - index, - } + fn into_usize(self) -> usize { + self as usize } -} - -impl fmt::Display for AbsIndex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{}:{}]", self.layer, self.index) - } -} - -impl Sub for LodIndex { - type Output = LodIndex; - fn sub(self, rhs: LodIndex) -> Self::Output { - LodIndex { - data: self.data - rhs.data /*fast but has overflow issues*/ - } - } -} - -impl Add for LodIndex { - type Output = LodIndex; - fn add(self, rhs: LodIndex) -> Self::Output { - LodIndex { - data: self.data + rhs.data /*fast but has overflow issues*/ - } - } -} - -pub const fn two_pow_u(n: u8) -> u16 { - 1 << n -} - -pub fn relative_to_1d(child_lod: LodIndex, parent_lod: LodIndex, child_layer: u8, relative_size: Vec3) -> usize { - let width = two_pow_u(child_layer) as u32; - let index = (child_lod.get() - parent_lod.get()).map(|e| e / width); - (index[0] * relative_size[2] * relative_size[1] + index[1] * relative_size[2] + index[2]) as usize -} - -pub fn min(lhs: LodIndex, rhs: LodIndex) -> LodIndex { - let lhs = lhs.get(); - let rhs = rhs.get(); - LodIndex::new(lhs.map2(rhs, |a,b| cmp::min(a,b))) -} - -pub fn max(lhs: LodIndex, rhs: LodIndex) -> LodIndex { - let lhs = lhs.get(); - let rhs = rhs.get(); - LodIndex::new(lhs.map2(rhs, |a,b| cmp::max(a,b))) -} - -/************* - TESTS -**************/ - -#[cfg(test)] -mod tests { - use crate::{ - lodstore::index::LodIndex, - }; - use vek::*; - - #[test] - fn setter_getter() { - let i = LodIndex::new(Vec3::new(0,0,0)); - assert_eq!(i.get(), Vec3::new(0,0,0)); - - let i = LodIndex::new(Vec3::new(1337,0,0)); - assert_eq!(i.get(), Vec3::new(1337,0,0)); - - let i = LodIndex::new(Vec3::new(0,1337,0)); - assert_eq!(i.get(), Vec3::new(0,1337,0)); - - let i = LodIndex::new(Vec3::new(0,0,1337)); - assert_eq!(i.get(), Vec3::new(0,0,1337)); - - let i = LodIndex::new(Vec3::new(1,1,1)); - assert_eq!(i.get(), Vec3::new(1,1,1)); - - let i = LodIndex::new(Vec3::new(262143,262143,262143)); - assert_eq!(i.get(), Vec3::new(262143,262143,262143)); - - let i = LodIndex::new(Vec3::new(262144,262144,262144)); //overflow - assert_eq!(i.get(), Vec3::new(0,0,0)); - - let i = LodIndex::new(Vec3::new(42,1337,69)); - assert_eq!(i.get(), Vec3::new(42,1337,69)); - } - - #[test] - fn align() { - let i = LodIndex::new(Vec3::new(1337,0,0)).align_to_layer_id(4); - assert_eq!(i.get(), Vec3::new(1328,0,0)); - - let i = LodIndex::new(Vec3::new(1337,1800,0)).align_to_layer_id(5); - assert_eq!(i.get(), Vec3::new(1312,1792,0)); - - let i = LodIndex::new(Vec3::new(1337,0,50)).align_to_layer_id(3); - assert_eq!(i.get(), Vec3::new(1336,0,48)); - - let i = LodIndex::new(Vec3::new(1335,0,0)).align_to_layer_id(3); - assert_eq!(i.get(), Vec3::new(1328,0,0)); - - let i = LodIndex::new(Vec3::new(31337,22000,25000)).align_to_layer_id(7); - assert_eq!(i.get(), Vec3::new(31232,21888,24960)); - - let i = LodIndex::new(Vec3::new(31337,22000,25000)).align_to_layer_id(0); - assert_eq!(i.get(), Vec3::new(31337,22000,25000)); - - let i = LodIndex::new(Vec3::new(0,0,0)).align_to_layer_id(4); - assert_eq!(i.get(), Vec3::new(0,0,0)); - } - - #[test] - fn get_highest_layer_that_fits() { - let i = LodIndex::new(Vec3::new(0,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 15); - let i = LodIndex::new(Vec3::new(1,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 0); - let i = LodIndex::new(Vec3::new(2,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 1); - let i = LodIndex::new(Vec3::new(3,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 0); - let i = LodIndex::new(Vec3::new(4,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 2); - let i = LodIndex::new(Vec3::new(5,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 0); - - let i = LodIndex::new(Vec3::new(1337,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 0); - - let i = LodIndex::new(Vec3::new(1337,1800,0)); - assert_eq!(i.get_highest_layer_that_fits(), 0); - - let i = LodIndex::new(Vec3::new(1338,0,50)); - assert_eq!(i.get_highest_layer_that_fits(), 1); - - let i = LodIndex::new(Vec3::new(1336,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 3); - - let i = LodIndex::new(Vec3::new(31348,22000,25000)); - assert_eq!(i.get_highest_layer_that_fits(), 2); - - let i = LodIndex::new(Vec3::new(0,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 15); - - let i = LodIndex::new(Vec3::new(65536,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 15); - - let i = LodIndex::new(Vec3::new(32768,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 15); - - let i = LodIndex::new(Vec3::new(16384,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 14); - - let i = LodIndex::new(Vec3::new(8192,0,0)); - assert_eq!(i.get_highest_layer_that_fits(), 13); - - let i = LodIndex::new(Vec3::new(65536,0,8192)); - assert_eq!(i.get_highest_layer_that_fits(), 13); - } -} - +} \ No newline at end of file diff --git a/worldsim/src/lodstore/lodpos.rs b/worldsim/src/lodstore/lodpos.rs new file mode 100644 index 0000000000..db77de0388 --- /dev/null +++ b/worldsim/src/lodstore/lodpos.rs @@ -0,0 +1,292 @@ +use vek::*; +use std::ops::Sub; +use std::ops::Add; +use std::cmp; +use std::fmt; + +/* +A region owns the Values from in (0, 2048) in steps of 1/32. +But because regions can also subscribe we add support to the range (0, 2048*3). +which is 13 bits for the digits before the decimal point and 5 bits for the digits after the decimal point. +We use our own LodPos type to store and compute based on these values, because u16 arithmetic (inside the owned area) is super easy to archive and allows us to optimize a lot. + + +-- lower neighbor +0 -> 0 +65535 -> 2047 31/32 +-- owned +65536 -> 2048 +131071 -> 4095 31/32 +-- upper neighbor +196607 -> 6143 31/32 +*/ + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub struct LodPos { + /* + bit 0..17 -> x + bit 18..35 -> y + bit 36..53 -> z + bit 54..63 -> unused + */ + data: u64, +} + +/*does not work on big endian!*/ +const BIT_X_MASK: u64 = 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011_1111_1111_1111_1111; +const BIT_Y_MASK: u64 = 0b0000_0000_0000_0000_0000_0000_0000_1111_1111_1111_1111_1100_0000_0000_0000_0000; +const BIT_Z_MASK: u64 = 0b0000_0000_0011_1111_1111_1111_1111_0000_0000_0000_0000_0000_0000_0000_0000_0000; +const BIT_X_MASK32: u32 = 0b0000_0000_0000_0011_1111_1111_1111_1111; +const BIT_Y_OFFSET: u8 = 18; +const BIT_Z_OFFSET: u8 = 36; + +//TODO: Optimize! +impl LodPos { + pub fn new(data: Vec3) -> Self { + let mut index = LodPos {data: 0}; + index.set(data); + index + } + + pub fn xyz(x: u32, y: u32, z: u32) -> Self { + LodPos {data: Self::encode(&x, &y, &z)} + } + + pub fn get(&self) -> Vec3 { + Vec3::from(Self::decode(&self.data)) + } + + pub fn set(&mut self, data: Vec3) { + self.data = Self::encode(&data.x, &data.y, &data.z); + } + + fn encode(x: &u32, y: &u32, z: &u32) -> u64 { + let x = (x & BIT_X_MASK32) as u64; + let y = ((y & BIT_X_MASK32) as u64 ) << BIT_Y_OFFSET; + let z = ((z & BIT_X_MASK32) as u64 ) << BIT_Z_OFFSET; + x + y + z + } + + fn decode(data: &u64) -> (u32, u32, u32) { + let x = (data & BIT_X_MASK) as u32; + let y = ((data & BIT_Y_MASK) >> BIT_Y_OFFSET ) as u32; + let z = ((data & BIT_Z_MASK) >> BIT_Z_OFFSET ) as u32; + (x,y,z) + } + + pub fn align_to_level(&self, layer: u8) -> LodPos { + let xyz = self.get(); + let f = two_pow_u(layer) as u32; + LodPos::new(xyz.map(|i| { + (i / f) * f + })) + } + + pub fn get_highest_level_that_fits(&self) -> u8 { + let pos = self.get(); + cmp::min( cmp::min(cmp::min(pos[0].trailing_zeros(), + pos[1].trailing_zeros()), pos[2].trailing_zeros()), 15) as u8 + } +} + +impl fmt::Display for LodPos { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let xyz = self.get(); + //write!(f, "({}|{}|{}) <{}>", xyz[0], xyz[1], xyz[2], self.data) + write!(f, "({}|{}|{})", xyz[0], xyz[1], xyz[2]) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub struct AbsIndex { + pub layer: u8, + pub index: usize, +} + +impl AbsIndex { + pub fn new(layer: u8, index: usize) -> Self { + AbsIndex { + layer, + index, + } + } +} + +impl fmt::Display for AbsIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}:{}]", self.layer, self.index) + } +} + +impl Sub for LodPos { + type Output = LodPos; + fn sub(self, rhs: LodPos) -> Self::Output { + LodPos { + data: self.data - rhs.data /*fast but has overflow issues*/ + } + } +} + +impl Add for LodPos { + type Output = LodPos; + fn add(self, rhs: LodPos) -> Self::Output { + LodPos { + data: self.data + rhs.data /*fast but has overflow issues*/ + } + } +} + +pub const fn two_pow_u(n: u8) -> u16 { + 1 << n +} +pub const fn two_pow_u32(n: u8) -> u32 { + 1 << n +} + +pub const fn multily_with_2_pow_n(a: usize, n: u8) -> usize { //return a * 2^n but fast + a << n +} + +pub fn relative_to_1d(child_lod: LodPos, parent_lod: LodPos, child_layer: u8, relative_size: Vec3) -> usize { + let width = two_pow_u32(child_layer) as u32; + let index = (child_lod.get() - parent_lod.get()).map(|e| e / width); + (index[0] * relative_size[2] * relative_size[1] + index[1] * relative_size[2] + index[2]) as usize +} + +pub fn min(lhs: LodPos, rhs: LodPos) -> LodPos { + let lhs = lhs.get(); + let rhs = rhs.get(); + LodPos::new(lhs.map2(rhs, |a,b| cmp::min(a,b))) +} + +pub fn max(lhs: LodPos, rhs: LodPos) -> LodPos { + let lhs = lhs.get(); + let rhs = rhs.get(); + LodPos::new(lhs.map2(rhs, |a,b| cmp::max(a,b))) +} + +/************* + TESTS +**************/ + +#[cfg(test)] +mod tests { + use crate::{ + lodstore::lodpos::LodPos, + lodstore::lodpos::two_pow_u32, + }; + use vek::*; + use test::Bencher; + + #[test] + fn setter_getter() { + let i = LodPos::xyz(0,0,0); + assert_eq!(i.get(), Vec3::new(0,0,0)); + + let i = LodPos::xyz(1337,0,0); + assert_eq!(i.get(), Vec3::new(1337,0,0)); + + let i = LodPos::xyz(0,1337,0); + assert_eq!(i.get(), Vec3::new(0,1337,0)); + + let i = LodPos::xyz(0,0,1337); + assert_eq!(i.get(), Vec3::new(0,0,1337)); + + let i = LodPos::xyz(1,1,1); + assert_eq!(i.get(), Vec3::new(1,1,1)); + + let i = LodPos::xyz(262143,262143,262143); + assert_eq!(i.get(), Vec3::new(262143,262143,262143)); + + let i = LodPos::xyz(262144,262144,262144); //overflow + assert_eq!(i.get(), Vec3::new(0,0,0)); + + let i = LodPos::xyz(42,1337,69); + assert_eq!(i.get(), Vec3::new(42,1337,69)); + } + + #[test] + fn align() { + let i = LodPos::xyz(1337,0,0).align_to_level(4); + assert_eq!(i.get(), Vec3::new(1328,0,0)); + + let i = LodPos::xyz(1337,1800,0).align_to_level(5); + assert_eq!(i.get(), Vec3::new(1312,1792,0)); + + let i = LodPos::xyz(1337,0,50).align_to_level(3); + assert_eq!(i.get(), Vec3::new(1336,0,48)); + + let i = LodPos::xyz(1335,0,0).align_to_level(3); + assert_eq!(i.get(), Vec3::new(1328,0,0)); + + let i = LodPos::xyz(31337,22000,25000).align_to_level(7); + assert_eq!(i.get(), Vec3::new(31232,21888,24960)); + + let i = LodPos::xyz(31337,22000,25000).align_to_level(0); + assert_eq!(i.get(), Vec3::new(31337,22000,25000)); + + let i = LodPos::xyz(0,0,0).align_to_level(4); + assert_eq!(i.get(), Vec3::new(0,0,0)); + } + + #[test] + fn get_highest_level_that_fits() { + let i = LodPos::xyz(0,0,0); + assert_eq!(i.get_highest_level_that_fits(), 15); + let i = LodPos::xyz(1,0,0); + assert_eq!(i.get_highest_level_that_fits(), 0); + let i = LodPos::xyz(2,0,0); + assert_eq!(i.get_highest_level_that_fits(), 1); + let i = LodPos::xyz(3,0,0); + assert_eq!(i.get_highest_level_that_fits(), 0); + let i = LodPos::xyz(4,0,0); + assert_eq!(i.get_highest_level_that_fits(), 2); + let i = LodPos::xyz(5,0,0); + assert_eq!(i.get_highest_level_that_fits(), 0); + + let i = LodPos::xyz(1337,0,0); + assert_eq!(i.get_highest_level_that_fits(), 0); + + let i = LodPos::xyz(1337,1800,0); + assert_eq!(i.get_highest_level_that_fits(), 0); + + let i = LodPos::xyz(1338,0,50); + assert_eq!(i.get_highest_level_that_fits(), 1); + + let i = LodPos::xyz(1336,0,0); + assert_eq!(i.get_highest_level_that_fits(), 3); + + let i = LodPos::xyz(31348,22000,25000); + assert_eq!(i.get_highest_level_that_fits(), 2); + + let i = LodPos::xyz(0,0,0); + assert_eq!(i.get_highest_level_that_fits(), 15); + + let i = LodPos::xyz(65536,0,0); + assert_eq!(i.get_highest_level_that_fits(), 15); + + let i = LodPos::xyz(32768,0,0); + assert_eq!(i.get_highest_level_that_fits(), 15); + + let i = LodPos::xyz(16384,0,0); + assert_eq!(i.get_highest_level_that_fits(), 14); + + let i = LodPos::xyz(8192,0,0); + assert_eq!(i.get_highest_level_that_fits(), 13); + + let i = LodPos::xyz(65536,0,8192); + assert_eq!(i.get_highest_level_that_fits(), 13); + } + + #[bench] + fn bench_access_two_pow(b: &mut Bencher) { + b.iter(|| two_pow_u32(6)); + } + + #[bench] + fn bench_access_align(b: &mut Bencher) { + let access = LodPos::xyz(0, 0, 0); + b.iter(|| access.align_to_level(6)); + } +} + diff --git a/worldsim/src/lodstore/mod.rs b/worldsim/src/lodstore/mod.rs index 39c4d8244f..6b82d63994 100644 --- a/worldsim/src/lodstore/mod.rs +++ b/worldsim/src/lodstore/mod.rs @@ -1,5 +1,6 @@ -pub mod index; +pub mod lodpos; pub mod area; +pub mod index; pub mod newdata; pub mod data; pub mod delta; diff --git a/worldsim/src/lodstore/newdata.rs b/worldsim/src/lodstore/newdata.rs index d86a7c04ba..5f5a668f85 100644 --- a/worldsim/src/lodstore/newdata.rs +++ b/worldsim/src/lodstore/newdata.rs @@ -1,10 +1,12 @@ use super::area::LodArea; use super::delta::LodDelta; -use super::index::{self, relative_to_1d, two_pow_u, AbsIndex, LodIndex}; +use super::lodpos::{self, multily_with_2_pow_n, relative_to_1d, two_pow_u32, AbsIndex, LodPos}; +use super::index::ToOptionUsize; +use fxhash::FxHashMap; use std::collections::HashMap; -use std::u32; +use std::{u16, u32}; use vek::*; -use std::marker::PhantomData; + /* Terminology: - Layer: the layer of the LoDTree, a tree can have n layers, every layer contains their child layer, except for the last one. @@ -15,61 +17,65 @@ use std::marker::PhantomData; - Index: This refers to the actually storage for the index for the next layer (often a u16,u32). The Index is used to find the child in a spare storage. - Key: always refers to the storage of a LAYER. Any keyword with KEY is either of type usize or LodPos. - - Prefix P always means parent, Prexix C always child, no prefix means for this layer. traits: - - IndexStore: Every layer must implement this for either KEY = usize or KEY = LodPos and INDEX is often u16/u32. depending on the store of the parent detail. - It is accessed by parent layer to store the index when a detail is added or removed. - Every IndexStore has a parent, and a constant OWN_PER_PARENT which says, how many details fit into one element of the parent. - - DetailStore: Every layer must implement this for either KEY = usize or KEY = LodPos, independent from the parent. - This is used to store the actual detail of every layer. - - Nestable: All layers, except the lowest one implement this trait. It links the below layer to interact with the child layer. - !!Calculations will be implemented on these 3 Stores, rather than the actual structs to reduce duplciate coding!! + - Layer: Every layer must implement this. KEY is the storage Type and either usize/LodPos. Layer is also defined here. + - ParentLayer: Is a Layer that contains a CHILD layer and some const functions based on their const properties + - IndexStore: Every layer must implement this for their Layer::KEY and INDEX is often u16/u32. + The index is accessed by this layer to get the corresponding child. + Every Indexstore is a ParentLayer. + - DetailStore: Every layer must implement this for their KEY. + This is used to store the actual DETAIL of every layer. + !!Calculations will be implemented on these 2 Stores, rather than the actual structs to reduce duplciate coding!! + - ToOptionUsize: to store INDEX in z16/u32 efficiently and move up to usize on calculation - Traversable: trait is used to get child layer and child Index for a concrete position. - Materializeable: trait is used to actually return a Detail for a concrete position. - Actual structs regarding of position in the chain. They represent the Layers and contain the Details, they implement (some of) the 3 Store traits - Naming Scheme is [Nest]Layer - - VecVecLayer/VecHashLayer: Vec Leaf Layers that have a vec/hash index and dont have a child layer. - - VecVecNestLayer/VecHashNestLayer: Vec Layers that have a vec/hash index and are middle layers - - HashNoneNestLayer: Hash Layer that has no index and must be parent layer + Actual structs regarding of position in the chain. They represent the Layers and contain the Details, they implement (some of) the 2 Store traits + Naming Scheme is [Nest]Layer + - VecLayer: KEY=usize, stores in Vec, leaf layer + - HashLayer:KEY=LodPos, stores in Vec, leaf layer + - VecNestLayer: KEY=usize, stores in Vec, has childs + - HashNestLayer: KEY=LodPos, stores in Vec, has childs Result Structs: - - LayerResult: Is used to access a layer meta information or Detail via LoDTree.traverse().get().get().get().mat(). - When LoDTree.traverse() returns a LayerResult. + - HashIter/VecIter: Is used to access a layer meta information or Detail via LoDTree.trav().get().get().get().mat(). + When LoDTree.trav() returns a HashIter. + It keeps information to next layer to not recalculate it */ - -pub type LodPos = LodIndex; - -pub trait Index {} - -pub trait Key { - fn from_index(index: T) -> Self; - fn to_index(self) -> T; -} - -pub trait IndexStore { - fn load(&self, pos: LodPos) -> usize; -} - -pub trait DetailStore { - type KEY: Key; - type DETAIL; +pub trait Layer { + type KEY; const LEVEL: u8; - - DER RETURNWERT MUSS EIN EIGENER TYPE SEIN JE NACHDEM VEC ODER HASH - - fn load(&self, pos: LodPos) -> usize; } -pub trait Nestable: DetailStore { - type NESTED: IndexStore::KEY> + DetailStore; - - fn nested(&self) -> &Self::NESTED; +pub trait ParentLayer: Layer { + type CHILD: Layer; + fn child(&self) -> &Self::CHILD; + fn CHILDS_PER_OWN_TOTAL() -> usize { + two_pow_u32(Self::LOG2_OF_CHILDS_PER_OWN_TOTAL()) as usize + } + fn LOG2_OF_CHILDS_PER_OWN_TOTAL() -> u8 { + 3 * ({ Self::LEVEL } - Self::CHILD::LEVEL) + } + fn CHILDS_PER_OWN() -> Vec3 { + Vec3 { + x: two_pow_u32(Self::LEVEL - Self::CHILD::LEVEL) as u32, + y: two_pow_u32(Self::LEVEL - Self::CHILD::LEVEL) as u32, + z: two_pow_u32(Self::LEVEL - Self::CHILD::LEVEL) as u32, + } + } } -//TODO: make LodTree trait and make traverse a function which returns a LayerResult to the TOP Layer (and not one layer below that), or call it iter, lets see +pub trait IndexStore: ParentLayer { + type INDEX: ToOptionUsize; + fn load(&self, key: Self::KEY) -> Self::INDEX; +} + +pub trait DetailStore: Layer { + type DETAIL; + fn load(&self, key: Self::KEY) -> &Self::DETAIL; +} pub trait Traversable { fn get(self) -> C; @@ -78,280 +84,269 @@ pub trait Materializeable { fn mat(self) -> T; } -//CK is childs key of IndexStore, its needed for Traversable, but IndexStore cannot be a dependency, because of last node which acets materializeable but not Traversable -pub struct LayerResult<'a, N: DetailStore> { - child: &'a N, - wanted: LodPos, - key: N::KEY, -} - //####################################################### +#[derive(Default)] pub struct VecLayer { pub detail: Vec, } +#[derive(Default)] pub struct HashLayer { - pub detail: HashMap, + pub detail: FxHashMap, } - -pub struct VecNestLayer { +#[derive(Default)] +pub struct VecNestLayer { pub detail: Vec, pub index: Vec, - pub nested: N, + pub child: C, +} +#[derive(Default)] +pub struct HashNestLayer { + pub detail_index: FxHashMap, + pub child: C, } -pub struct HashNestLayer { - pub detail_index: HashMap, - pub nested: N, +pub struct HashIter<'a, C: DetailStore> { + layer: &'a C, + wanted: LodPos, + layer_lod: LodPos, //LodPos aligned to layer::LEVEL +} +pub struct VecIter<'a, C: DetailStore> { + layer: &'a C, + wanted: LodPos, + layer_lod: LodPos, //LodPos aligned to layer::LEVEL + layer_key: usize, } #[rustfmt::skip] -impl IndexStore for VecNestLayer { - fn load(&self, pos: LodPos) -> usize { - let adjusted_pos = pos; - let pos_offset = 0; - let childs_per_own = 8; - let u1 = ( self.index[adjusted_pos * childs_per_own ) as usize + pos_offset; - return u1; - } +impl Layer for VecLayer { + type KEY = ( usize ); const LEVEL: u8 = { L }; } #[rustfmt::skip] -impl IndexStore for HashNestLayer { - fn load(&self, pos: LodPos) -> usize { - let adjusted_pos = pos; - let pos_offset = 0; - let childs_per_own = 8; - let u1 = ( self.index[adjusted_pos * childs_per_own ) as usize + pos_offset; - return u1; - } +impl Layer for HashLayer { + type KEY = ( LodPos ); const LEVEL: u8 = { L }; } - - #[rustfmt::skip] -impl IndexStore for HashNestLayer { - type KEY = LodPos; type INDEX=PI; const OWN_PER_PARENT: usize = 1337; - fn load(&self, key: LodPos) -> PI { *self.index.get(&key).unwrap() } - fn store(&mut self, key: LodPos, index: PI) { self.index.insert(key, index); } +impl Layer for VecNestLayer { + type KEY = ( usize ); const LEVEL: u8 = { L }; +} +#[rustfmt::skip] +impl Layer for HashNestLayer { + type KEY = ( LodPos ); const LEVEL: u8 = { L }; } #[rustfmt::skip] -impl + DetailStore, T, PI: Copy, const L: u8> IndexStore for VecHashNestLayer { - type KEY = LodPos; type INDEX=PI; const OWN_PER_PARENT: usize = 4096; - fn load(&self, key: LodPos) -> PI { *self.index.get(&key).unwrap() } - fn store(&mut self, key: LodPos, index: PI) { self.index.insert(key, index); } +impl ParentLayer for VecNestLayer { + type CHILD = C; + fn child(&self) -> &Self::CHILD { &self.child } +} +#[rustfmt::skip] +impl ParentLayer for HashNestLayer { + type CHILD = C; + fn child(&self) -> &Self::CHILD { &self.child } } -#[rustfmt::skip] -impl DetailStore for VecVecLayer { - type KEY = usize; type DETAIL=T; const LEVEL: u8 = { L }; - fn load(&self, key: usize) -> &T { self.detail.get(key).unwrap() } - fn load_mut(&mut self, key: usize) -> &mut T { self.detail.get_mut(key).unwrap() } - fn store(&mut self, key: usize, detail: T) { self.detail.insert(key, detail); } -} -#[rustfmt::skip] -impl + DetailStore, T, PI: Copy, const L: u8> DetailStore for VecVecNestLayer { - type KEY = usize; type DETAIL=T; const LEVEL: u8 = { L }; - fn load(&self, key: usize) -> &T { self.detail.get(key).unwrap() } - fn load_mut(&mut self, key: usize) -> &mut T { self.detail.get_mut(key).unwrap() } - fn store(&mut self, key: usize, detail: T) { self.detail.insert(key, detail); } -} -#[rustfmt::skip] -impl DetailStore for VecHashLayer { - type KEY = usize; type DETAIL=T; const LEVEL: u8 = { L }; - fn load(&self, key: usize) -> &T { self.detail.get(key).unwrap() } - fn load_mut(&mut self, key: usize) -> &mut T { self.detail.get_mut(key).unwrap() } - fn store(&mut self, key: usize, detail: T) { self.detail.insert(key, detail); } -} -#[rustfmt::skip] -impl + DetailStore, T, PI: Copy, const L: u8> DetailStore for VecHashNestLayer { - type KEY = usize; type DETAIL=T; const LEVEL: u8 = { L }; - fn load(&self, key: usize) -> &T { self.detail.get(key).unwrap() } - fn load_mut(&mut self, key: usize) -> &mut T { self.detail.get_mut(key).unwrap() } - fn store(&mut self, key: usize, detail: T) { self.detail.insert(key, detail); } -} -#[rustfmt::skip] -impl + DetailStore, T, const L: u8> DetailStore for HashNoneNestLayer { - type KEY = LodPos; type DETAIL=T; const LEVEL: u8 = { L }; - fn load(&self, key: LodPos) -> &T { self.detail.get(&key).unwrap() } - fn load_mut(&mut self, key: LodPos) -> &mut T { self.detail.get_mut(&key).unwrap() } - fn store(&mut self, key: LodPos, detail: T) { self.detail.insert(key, detail); } -} - -#[rustfmt::skip] -impl + DetailStore, T, PI: Copy, const L: u8> Nestable for VecVecNestLayer { - type NESTED=N; - fn nested(&self) -> &N { &self.nested } -} -#[rustfmt::skip] -impl + DetailStore, T, PI: Copy, const L: u8> Nestable for VecHashNestLayer { - type NESTED=N; - fn nested(&self) -> &N { &self.nested } -} -#[rustfmt::skip] -impl + DetailStore, T, const L: u8> Nestable for HashNoneNestLayer { - type NESTED=N; - fn nested(&self) -> &N { &self.nested } -} - -//####################################################### - -impl + DetailStore, T, const L: u8> HashNoneNestLayer -{ - pub fn trav<'a>(&'a self, pos: LodPos) -> LayerResult<'a, Self> { - LayerResult { - child: self, +impl HashNestLayer { + fn trav(&self, pos: LodPos) -> HashIter { + HashIter { + layer: &self, wanted: pos, - key: pos.align_to_layer_id(Self::LEVEL), + layer_lod: pos.align_to_level({ L }), } } } -/*impl<'a, N: DetailStore + Nestable> -Traversable::KEY>> for LayerResult<'a, N, ::KEY> -where N::NESTED: IndexStore, - ::KEY: Copy, - ::KEY: KeyConvertable::KEY, INDEX=::INDEX> -{ - fn get(self) -> LayerResult<'a, N::NESTED, ::KEY> { - println!("{}", N::LEVEL); - let child = self.child.nested(); - let key = self.key; - //let index = self.index.align_to_layer_id(N::LEVEL); - LayerResult { - child, - wanted: self.wanted, - key: key.uncompress(&IndexStore::load(child, key)), - } - } -}*/ - -impl<'a, N: Nestable + DetailStore> -Traversable> for LayerResult<'a, N> - where N::NESTED: IndexStore, - ::KEY: Copy, -{ - fn get(self) -> LayerResult<'a, N::NESTED> { - println!("{}", N::LEVEL); - unimplemented!(); - } -} - -/* -impl<'a, N: Nestable> -Traversable> for LayerResult<'a, N> -where N::NESTED: IndexStore, - ::KEY: Copy, - ::KEY: KeyConvertable::KEY, INDEX=::INDEX>, -{ - fn get(self) -> LayerResult<'a, N::NESTED> { - println!("{}", N::LEVEL); - let child = self.child.nested(); - let key = self.key; - //let index = self.index.align_to_layer_id(N::LEVEL); - LayerResult { - child, - wanted: self.wanted, - key: key.uncompress(&IndexStore::load(child, key)), - } - } -} -*/ - -impl<'a, N: IndexStore + DetailStore> Materializeable for LayerResult<'a, N> { - fn mat(self) -> N::DETAIL { - unimplemented!(); - } -} - #[rustfmt::skip] -pub type ExampleDelta = +impl IndexStore for VecNestLayer { + type INDEX = I; + fn load(&self, key: Self::KEY) -> Self::INDEX { self.index[key] } +} +#[rustfmt::skip] +impl IndexStore for HashNestLayer { + type INDEX = I; + fn load(&self, key: Self::KEY) -> Self::INDEX { + debug_assert_eq!(key, key.align_to_level({ L })); + self.detail_index[&key].1 + } +} + +#[rustfmt::skip] +impl DetailStore for VecNestLayer { + type DETAIL = T; + fn load(&self, key: Self::KEY) -> &Self::DETAIL { + &self.detail[key] + } +} +#[rustfmt::skip] +impl DetailStore for HashNestLayer { + type DETAIL = T; + fn load(&self, key: LodPos) -> &Self::DETAIL { + debug_assert_eq!(key, key.align_to_level({ L })); + &self.detail_index[&key].0 + } +} +#[rustfmt::skip] +impl DetailStore for VecLayer { + type DETAIL = T; + fn load(&self, key: usize) -> &Self::DETAIL { + &self.detail[key] + } +} +#[rustfmt::skip] +impl DetailStore for HashLayer { + type DETAIL = T; + fn load(&self, key: LodPos) -> &Self::DETAIL { + debug_assert_eq!(key, key.align_to_level({ L })); + &self.detail[&key] + } +} + +impl<'a, L: DetailStore + IndexStore> Traversable> + for HashIter<'a, L> +where + L::CHILD: DetailStore, { + fn get(self) -> VecIter<'a, L::CHILD> { + let child_lod = self.wanted.align_to_level(L::CHILD::LEVEL ); + let pos_offset = relative_to_1d(child_lod, self.layer_lod, L::CHILD::LEVEL, L::CHILDS_PER_OWN()); + let layer_key = ( multily_with_2_pow_n( IndexStore::load(self.layer, self.layer_lod).into_usize(), L::LOG2_OF_CHILDS_PER_OWN_TOTAL()) ) + pos_offset; + VecIter { + layer: self.layer.child(), + wanted: self.wanted, + layer_key, + layer_lod: child_lod, + } + } +} + +impl<'a, L: DetailStore + IndexStore> Traversable> + for VecIter<'a, L> +where + L::CHILD: DetailStore, { + fn get(self) -> VecIter<'a, L::CHILD> { + let child_lod = self.wanted.align_to_level(L::CHILD::LEVEL ); + let pos_offset = relative_to_1d(child_lod, self.layer_lod, L::CHILD::LEVEL, L::CHILDS_PER_OWN()); + let layer_key = ( multily_with_2_pow_n( IndexStore::load(self.layer, self.layer_key).into_usize(), L::LOG2_OF_CHILDS_PER_OWN_TOTAL()) ) + pos_offset; + VecIter { + layer: self.layer.child(), + wanted: self.wanted, + layer_key, + layer_lod: child_lod, + } + } +} + +impl<'a, L: DetailStore> Materializeable<&'a L::DETAIL> for HashIter<'a, L> { + fn mat(self) -> &'a L::DETAIL { + DetailStore::load(self.layer, self.layer_lod) + } +} + +impl<'a, L: DetailStore> Materializeable<&'a L::DETAIL> for VecIter<'a, L> { + fn mat(self) -> &'a L::DETAIL { + DetailStore::load(self.layer, self.layer_key) + } +} + +#[rustfmt::skip] +pub type ExampleData = HashNestLayer< VecNestLayer< VecNestLayer< VecLayer< - (), 0 - > ,(), u16, 4 - > ,Option<()> , u32, 9 - > ,() ,u16. 13 + i8, 0 + > ,Option<()>, u16, 2 + > ,() , u32, 3 + > ,() ,u16, 4 >; - - -/* - -UNS INTERESSIERT DOCH GARNICHT der speicher DES INDEXES, OB ES EIN LodPOS oder u8 Index ist, es interessiert mich was für eine -ART INDEX DAS IST!!! -DIRECT HASH LOOKUP? -NESTED VEC LOOKUP! -und unterschiedliche indexe (index trait) haben unterschliedlichen speicher undder verhält sich unterschiedlich !!!!!!! - -A (data: Hashmap) -B (data: Vec, Index: Hashmap) -C (data: Vec, Index: Vec) -D (data: Vec, Index: Vec) - -A.data = [1 entry] -B.data = [8 entries] -C.data = [64 entries] -D.data = [8 entries] -B.index = [0] <- every entry in A has 8 childs. there are 1 entry in A, it has ... 8 child. The first of his childs can be found at 0*8 in B.data -C.index = [0, 1, 2, 3, 4, 5, 6, 7] <- because created in order, these are in order to, they mark the positions i*8 = [0,8,16,24,32,4,48,56] in C.data -D.index = [/,/,/,/,0,/,/,/,<56 more />] <- the first 4 od C dont have childs, then 1 C has 8 childs starting at 0*8 in D.data, the last of the 59 are again empty - - -NEUE IDEE: IndexStore - -DirectHashLookup { - store() -> - load(LodPos) -> LodAbs -} - -NestedVecLokup { - store(DIESE VERDAMTEN PARAMETER SIND NICHT GLEICH) -> - load(LodPos) -> LodPos -} - - - - - -Tree->iter() -> A() -A.mat() -> A.data[a.pos] -a.pos = B.index[LodPos] - -A.get() -> B() -B.mat() -> B.data[b.pos] -b.pos = C.index[] - - -*/ - #[cfg(test)] mod tests { use crate::lodstore::newdata::*; + use test::Bencher; - #[test] - fn newdata() { - let x = ExampleDelta { - detail: HashMap::new(), - nested: VecHashNestLayer { - detail: Vec::new(), - index: HashMap::new(), - nested: VecVecNestLayer { - detail: Vec::new(), - index: Vec::new(), - nested: VecVecLayer { - detail: Vec::new(), - index: Vec::new(), + fn gen_simple_example() -> ExampleData { + let mut detail_index = FxHashMap::default(); + detail_index.insert(LodPos::xyz(0, 0, 0), ((), 0)); + ExampleData { + detail_index, + child: VecNestLayer { + detail: vec!((),(),()), + index: vec!(0,1,u32::MAX), + child: VecNestLayer { + detail: vec!(None,None,None,Some(()),Some(()),None,None,None,None,None,None,None,None,None,None,None), + index: vec!(0,u16::MAX,u16::MAX,0,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX,u16::MAX), + child: VecLayer { + detail: vec!(7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), }, }, }, - }; - let i = LodPos::new(Vec3::new(0, 1, 2)); - let y = x.trav(i); - let ttc = y.get().get().get(); - let tt = ttc.mat(); + } } -} -// TODO: instead of storing the absolute index in index, we store (index / number of entities), which means a u16 in Block can not only hold 2 full Subblocks (32^3 subblocks per block). but the full 2^16-1 ones. + + #[test] + fn compilation() { + let x = ExampleData::default(); + let i = LodPos::xyz(0, 1, 2); + if false { + let y = x.trav(i); + let ttc = y.get().get().get(); + let tt = ttc.mat(); + } + } + + #[test] + fn access_first_element() { + let x = gen_simple_example(); + let i = LodPos::xyz(0, 0, 0); + assert_eq!(*x.trav(i).get().get().get().mat(), 7_i8); + } + + #[test] + fn access_simple_elements() { + let x = gen_simple_example(); + assert_eq!(*x.trav(LodPos::xyz(0, 0, 0)).get().get().get().mat(), 7_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 0, 1)).get().get().get().mat(), 6_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 0, 2)).get().get().get().mat(), 5_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 0, 3)).get().get().get().mat(), 4_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 1, 0)).get().get().get().mat(), 3_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 1, 1)).get().get().get().mat(), 2_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 1, 2)).get().get().get().mat(), 1_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 1, 3)).get().get().get().mat(), 0_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 3, 0)).get().get().get().mat(), 0_i8); + assert_eq!(*x.trav(LodPos::xyz(1, 0, 0)).get().get().get().mat(), 0_i8); + assert_eq!(*x.trav(LodPos::xyz(0, 2, 0)).get().get().get().mat(), 0_i8); + } + + #[bench] + fn bench_access_trav(b: &mut Bencher) { + let x = gen_simple_example(); + let access = LodPos::xyz(0, 0, 0); + b.iter(|| x.trav(access)); + } + + #[bench] + fn bench_access_3(b: &mut Bencher) { + let x = gen_simple_example(); + let access = LodPos::xyz(0, 0, 0); + b.iter(|| x.trav(access).mat()); + } + + #[bench] + fn bench_access_0(b: &mut Bencher) { + let x = gen_simple_example(); + let access = LodPos::xyz(0, 0, 0); + b.iter(|| x.trav(access).get().get().get().mat()); + } + + #[bench] + fn bench_access_0_best_time(b: &mut Bencher) { + let x = gen_simple_example(); + let access = LodPos::xyz(0, 0, 0); + for _ in 0..10000 { + //fill up the caches + x.trav(access).get().get().get().mat(); + } + b.iter(|| x.trav(access).get().get().get().mat()); + } +} \ No newline at end of file diff --git a/worldsim/src/region/lod/example.rs b/worldsim/src/region/lod/example.rs index cb38cef11c..06c1cd8264 100644 --- a/worldsim/src/region/lod/example.rs +++ b/worldsim/src/region/lod/example.rs @@ -2,14 +2,16 @@ use crate::lodstore::{ LodData, LodConfig, data::CacheLine, - index::LodIndex, - index::AbsIndex, + lodpos::LodPos, + lodpos::AbsIndex, area::LodArea, delta::LodDelta, }; use vek::*; use std::u32; +pub type LodIndex = LodPos; + #[derive(Clone)] pub struct Example9 { data: [u8; 700], @@ -215,14 +217,15 @@ mod tests { region::lod::example::ExampleLodConfig, region::lod::example::*, lodstore::LodData, - lodstore::index::LodIndex, - lodstore::index, + lodstore::lodpos::LodPos, + lodstore::lodpos, }; use std::{thread, time, mem::size_of}; use vek::*; use rand::Rng; use rand::ThreadRng; use test::Bencher; + pub type LodIndex = LodPos; fn randIndex(rng: &mut ThreadRng) -> LodIndex { let x: u16 = rng.gen(); @@ -239,10 +242,10 @@ mod tests { let mut rng = rand::thread_rng(); let mut delta = ExampleDelta::new(); let mut result = Example::new(); - let abs9 = (index::two_pow_u(15-13) as u64).pow(3); - let abs5 = (index::two_pow_u(15-9) as u64).pow(3); - let abs0 = (index::two_pow_u(15-4) as u64).pow(3); - let abs_4 = (index::two_pow_u(15) as u64).pow(3); + let abs9 = (lodpos::two_pow_u(15-13) as u64).pow(3); + let abs5 = (lodpos::two_pow_u(15-9) as u64).pow(3); + let abs0 = (lodpos::two_pow_u(15-4) as u64).pow(3); + let abs_4 = (lodpos::two_pow_u(15) as u64).pow(3); let p_e9 = 1.0+p_foreign; let p_e5 = p_e9*p_e5; let p_e0 = p_e5*p_e0; @@ -252,7 +255,7 @@ mod tests { let act0 = (abs0 as f32 * p_e0 ) as u32; let act_4 = (abs_4 as f32 * p_e_4 ) as u32; - let w9 = index::two_pow_u(13) as u32; + let w9 = lodpos::two_pow_u(13) as u32; result.layer13 = vec![Example9::new(); 8*8*8]; result.child13 = vec![u32::MAX; 8*8*8]; println!("size test {} -- {}", size_of::(), size_of::>()); @@ -267,19 +270,19 @@ mod tests { println!("creating Region with {} 5er, {} 0er, {} -4er", act5, act0 , act_4); while result.layer9.len() < act5 as usize { let index = randIndex(&mut rng); - let low = index.align_to_layer_id(9); + let low = index.align_to_level(9); let area = LodArea::new(low, low); result.make_at_least(area,9, Some(&mut delta)); } while result.layer4.len() < act0 as usize { let index = randIndex(&mut rng); - let low = index.align_to_layer_id(4); + let low = index.align_to_level(4); let area = LodArea::new(low, low); result.make_at_least(area, 4, Some(&mut delta)); } while result.layer0.len() < act_4 as usize { let index = randIndex(&mut rng); - let low = index.align_to_layer_id(0); + let low = index.align_to_level(0); let area = LodArea::new(low, low); result.make_at_least(area, 0, Some(&mut delta)); } diff --git a/worldsim/src/region/lod/npcs.rs b/worldsim/src/region/lod/npcs.rs index d905648eee..4ec479837e 100644 --- a/worldsim/src/region/lod/npcs.rs +++ b/worldsim/src/region/lod/npcs.rs @@ -1,12 +1,13 @@ use crate::lodstore::{ LodData, LodConfig, - index::LodIndex, + index::LodPos, index::AbsIndex, delta::LodDelta, }; use vek::*; use std::u32; +pub type LodIndex = LodPos; #[derive(Debug, Clone)] pub struct Region9 { diff --git a/worldsim/src/region/lod/terrain.rs b/worldsim/src/region/lod/terrain.rs index 98e54c794a..37e3bed9fc 100644 --- a/worldsim/src/region/lod/terrain.rs +++ b/worldsim/src/region/lod/terrain.rs @@ -1,12 +1,13 @@ use crate::lodstore::{ LodData, LodConfig, - index::LodIndex, - index::AbsIndex, + lodpos::LodPos, + lodpos::AbsIndex, delta::LodDelta, }; use vek::*; use std::u32; +pub type LodIndex = LodPos; #[derive(Debug, Clone)] pub struct Region9 {